keyring-agent-core 0.2.12 → 0.2.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +12 -8
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- "use strict";var cr=Object.create;var hn=Object.defineProperty;var ur=Object.getOwnPropertyDescriptor;var dr=Object.getOwnPropertyNames;var mr=Object.getPrototypeOf,pr=Object.prototype.hasOwnProperty;var hr=(c,e)=>{for(var t in e)hn(c,t,{get:e[t],enumerable:!0})},uo=(c,e,t,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of dr(e))!pr.call(c,r)&&r!==t&&hn(c,r,{get:()=>e[r],enumerable:!(n=ur(e,r))||n.enumerable});return c};var gr=(c,e,t)=>(t=c!=null?cr(mr(c)):{},uo(e||!c||!c.__esModule?hn(t,"default",{value:c,enumerable:!0}):t,c)),fr=c=>uo(hn({},"__esModule",{value:!0}),c);var Ra={};hr(Ra,{AI_AGENT_TOOL_NAMES:()=>On,AgentCore:()=>Hn,ApproveTokenTool:()=>qt,BaseNftMessageTool:()=>fe,BaseSwapService:()=>Ue,BaseTool:()=>v,BaseWalletActionTool:()=>le,BuyTokenTool:()=>Vt,ChatHistory:()=>Yt,DEFAULT_CHAIN:()=>ge,DEFAULT_PROVIDER:()=>on,DEFAULT_RPC_BY_CHAIN:()=>ie,DebridgeAdapter:()=>vt,EstimatePoolYieldTool:()=>_t,GeminiProvider:()=>se,GeminiSearchAiTool:()=>mt,HEX_TO_PANTOGRAPH:()=>Co,KnowledgeBase:()=>jt,MoralisService:()=>E,NFTContractInfoTool:()=>ut,NFTMetadataTool:()=>dt,NFT_AGENT_TOOL_NAMES:()=>Bn,OpenAddLiquidityFormTool:()=>At,POOL_AGENT_TOOL_NAMES:()=>qn,PantographService:()=>ee,PoolByAddressTool:()=>yt,PoolDetailTool:()=>gt,PoolFallbackAiTool:()=>pt,PoolSearchTool:()=>ft,PoolService:()=>Me,PreviewAddLiquidityTool:()=>Pt,RelayAdapter:()=>St,Router:()=>zt,SUPPORTED_CHAINS:()=>$e,SUPPORTED_CHAINS_LABEL:()=>Ne,SWAP_PROVIDER_CONFIG:()=>Cn,SendNativeTool:()=>Bt,SendNftTool:()=>Gt,SendTokenTool:()=>Ot,Subagent:()=>j,SubgraphAddPoolTool:()=>Dt,SubgraphCoinPoolPairsTool:()=>It,SubgraphPoolByAddressTool:()=>Rt,SubgraphPoolByPositionIdTool:()=>Et,SubgraphPoolSearchTool:()=>Ct,SubgraphPositionDetailTool:()=>Nt,SubgraphTrendingPoolsTool:()=>Ut,Summarizer:()=>Kt,SwapServiceFactory:()=>Le,SwapTokenTool:()=>Ht,Synthesizer:()=>Qt,TOKEN_AGENT_TOOL_NAMES:()=>Mn,TRANSFER_TOPIC:()=>Ko,TRENDING_DEFAULT_BY_HEX_CHAIN:()=>Uo,TokenAnalyticsTool:()=>je,TokenHoldersTool:()=>Ke,TokenInfoTool:()=>Ye,TokenScoreTool:()=>ze,ToolRegistry:()=>Fe,TopGainersTool:()=>Xe,TopPoolsTool:()=>ht,TransactionByHashTool:()=>lt,TrendingTokensTool:()=>Qe,UNISWAP_CHAIN_SLUG:()=>sn,UpstashKnowledgeBase:()=>Re,WALLET_ACTION_AGENT_TOOL_NAMES:()=>Fn,WALLET_AGENT_TOOL_NAMES:()=>Ln,WalletApprovalsTool:()=>rt,WalletDefiPositionsTool:()=>at,WalletDefiProtocolPositionsTool:()=>it,WalletDefiSummaryTool:()=>st,WalletHistoryTool:()=>Ze,WalletNFTsTool:()=>ct,WalletNetWorthTool:()=>kn,WalletNftTransfersTool:()=>tt,WalletPnlSummaryTool:()=>nt,WalletPnlTool:()=>ot,WalletTokenBalancesTool:()=>Je,WalletTokenTransfersTool:()=>et,ZERO_ADDRESS:()=>O,buildRangePresets:()=>ao,buildUniswapPoolUrl:()=>oe,clampTick:()=>ve,configureSupportedChains:()=>yn,createAiAgent:()=>dn,createDefaultSubagents:()=>$n,createNftAgent:()=>un,createPoolAgent:()=>mn,createTokenAgent:()=>cn,createWalletActionAgent:()=>pn,createWalletAgent:()=>ln,ethCallAt:()=>Ho,ethCallByChain:()=>so,getAffiliateFee:()=>kt,getChainMeta:()=>te,getDefaultRpcUrl:()=>Us,getGatewayAddress:()=>Rn,getNativeTokenInfo:()=>xt,getPositionManagerAddress:()=>En,getProviderByChain:()=>_n,ingestKnowledgeBase:()=>ir,priceToTick:()=>De,resolveRpcUrl:()=>Tt,roundTickToSpacing:()=>Te,rpcCall:()=>xe,setRpcOverrides:()=>ro,swapServiceFactory:()=>ye,tickToPrice:()=>ue,toPantographChain:()=>en});module.exports=fr(Ra);var po=gr(require("axios"));var mo="0.2.12";var br=`keyring-agent-core/${mo} (Keyring Chatbot Agent; +https://keyring.app)`,wr=3e4,kr=typeof process<"u"&&process.versions?.node!=null,Yn=class{instance;constructor(e={}){this.instance=po.default.create({baseURL:e.baseURL,timeout:e.timeout??wr,validateStatus:()=>!0,headers:{Accept:"application/json",...kr?{"User-Agent":e.userAgent??br}:{},...e.headers}})}async request(e,t,n,r={}){let o=await this.instance.request({...r,method:e,url:t,data:n}),s={};for(let[a,i]of Object.entries(o.headers??{}))i!=null&&(s[a.toLowerCase()]=String(i));return{ok:o.status>=200&&o.status<300,status:o.status,statusText:o.statusText,data:o.data,headers:s}}get(e,t){return this.request("GET",e,void 0,t)}post(e,t,n){return this.request("POST",e,t,n)}put(e,t,n){return this.request("PUT",e,t,n)}patch(e,t,n){return this.request("PATCH",e,t,n)}delete(e,t){return this.request("DELETE",e,void 0,t)}},_=new Yn;function N(c){let{data:e,status:t,statusText:n}=c,r=n?`HTTP ${t} ${n}`:`HTTP ${t}`;if(typeof e=="string")return e.trim()||r;if(e&&typeof e=="object"){let o=e,s=i=>i&&typeof i=="object"?i.message:void 0,a=[o.message,o.errorMessage,o.error,o.error_description,o.detail,s(o.error),s(o.data)];for(let i of a)if(typeof i=="string"&&i.trim())return i.trim()}return r}var ho={string:"STRING",number:"NUMBER",boolean:"BOOLEAN",object:"OBJECT",array:"ARRAY"},Tr="https://nft-demo.keyring.app/api/gemini-stable",vr=new Set([408,429,500,502,503,504]),go=3e4,gn=c=>new Promise(e=>setTimeout(e,c));function fn(c,e,t){if(t!=null&&t>0)return Math.min(t,go);let n=Math.min(e*2**(c-1),go);return n/2+Math.random()*(n/2)}function Sr(c){if(!c)return null;let e=Number(c);if(Number.isFinite(e))return Math.max(0,e*1e3);let t=Date.parse(c);return Number.isNaN(t)?null:Math.max(0,t-Date.now())}var se=class{model;maxTokens;temperature;baseUrl;constructor(e){this.model=e.model??"gemini-2.5-flash-lite",this.maxTokens=e.maxTokens??4096,this.temperature=e.temperature??1,this.baseUrl=e.baseUrl??Tr}async chat(e,t,n){let{systemInstruction:r,contents:o}=this.toContents(e),{maxRetries:s=5,retryDelayMs:a=1e3}=n??{},i=o.length>0?o:[{role:"user",parts:[{text:"(continue)"}]}];if(n?._debug){console.log("[GeminiProvider] turn sequence:");for(let m of i){let p=m.parts.map(h=>h.functionCall?`fc:${h.functionCall.name}`:h.functionResponse?`fr:${h.functionResponse.name}`:`text:${(h.text??"").slice(0,40)}`).join(", ");console.log(` ${m.role}: [${p}]`)}}let l={contents:i,generationConfig:{maxOutputTokens:this.maxTokens,temperature:this.temperature}};r&&(l.systemInstruction=r),t?.length?(l.tools=[{functionDeclarations:this.toFunctionDeclarations(t)}],n?.forceToolUse&&(l.toolConfig={functionCallingConfig:{mode:"ANY"}})):n?.googleSearch&&(l.tools=[{googleSearch:{}}]);let u=`${this.baseUrl}/v1beta/models/${this.model}:generateContent`,d="Unknown error";for(let m=1;m<=s;m++){let p;try{p=await _.post(u,l,{headers:{"Content-Type":"application/json"}})}catch(b){if(d=b instanceof Error?b.message:String(b),m>=s)break;await gn(fn(m,a));continue}if(!p.ok){let b=typeof p.data=="string"?p.data:JSON.stringify(p.data)||p.statusText;if(d=`Gemini proxy error ${p.status}: ${b}`,!vr.has(p.status))throw new Error(d);if(m>=s)break;let T=Sr(p.headers["retry-after"]??null);await gn(fn(m,a,T));continue}if(p.data==null||typeof p.data!="object"){if(d="Gemini proxy returned invalid JSON",m>=s)break;await gn(fn(m,a));continue}let h=p.data,g=h.candidates?.[0];if(!g)return{text:"",toolCalls:[]};let f="",y=[],k=g.content?.parts;if(!Array.isArray(k)||k.length===0){let b=g.finishReason;if(!(b==="SAFETY"||b==="RECITATION")&&m<s){d=`Gemini returned an empty turn (finishReason=${b??"none"})`,await gn(fn(m,a));continue}return{text:"",toolCalls:[]}}for(let b of k)b.text&&(f+=b.text),b.functionCall&&y.push({toolName:b.functionCall.name,args:b.functionCall.args??{},callId:b.functionCall.id??`call_${Date.now()}_${Math.random().toString(36).slice(2,8)}`});let w=h.usageMetadata?{promptTokens:h.usageMetadata.promptTokenCount??0,completionTokens:h.usageMetadata.candidatesTokenCount??0,totalTokens:h.usageMetadata.totalTokenCount??0}:void 0;return{text:f,toolCalls:y,usage:w}}throw new Error(d)}toContents(e){let t=[],n=[];for(let a of e){if(a.role==="system"){t.push(a.content);continue}if(a.role==="tool"){n.push({role:"function",parts:[{functionResponse:{name:a.toolName??"unknown",response:{result:a.content},...a.toolCallId?{id:a.toolCallId}:{}}}]});continue}if(a.role==="assistant"&&a.toolCalls?.length){n.push({role:"model",parts:a.toolCalls.map(i=>({functionCall:{name:i.toolName,args:i.args,...i.callId?{id:i.callId}:{}}}))});continue}n.push({role:a.role==="assistant"?"model":"user",parts:[{text:a.content}]})}let r=[];for(let a of n){let i=r[r.length-1],l=a.parts.some(d=>d.functionCall),u=i?.parts.some(d=>d.functionCall);i&&i.role===a.role&&!l&&!u?i.parts.push(...a.parts):r.push({...a,parts:[...a.parts]})}let o=[];for(let a=0;a<r.length;a++){let i=r[a],l=o[o.length-1];i.role==="function"&&!l?.parts.some(d=>d.functionCall)||i.role==="model"&&i.parts.some(d=>d.functionCall)&&!(r[a+1]?.role==="function")||o.push(i)}for(;o.length>0&&o[0].role!=="user";)o.shift();return{systemInstruction:t.length?{role:"user",parts:[{text:t.join(`
1
+ "use strict";var cr=Object.create;var hn=Object.defineProperty;var ur=Object.getOwnPropertyDescriptor;var dr=Object.getOwnPropertyNames;var mr=Object.getPrototypeOf,pr=Object.prototype.hasOwnProperty;var hr=(c,e)=>{for(var t in e)hn(c,t,{get:e[t],enumerable:!0})},uo=(c,e,t,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of dr(e))!pr.call(c,r)&&r!==t&&hn(c,r,{get:()=>e[r],enumerable:!(n=ur(e,r))||n.enumerable});return c};var gr=(c,e,t)=>(t=c!=null?cr(mr(c)):{},uo(e||!c||!c.__esModule?hn(t,"default",{value:c,enumerable:!0}):t,c)),fr=c=>uo(hn({},"__esModule",{value:!0}),c);var Ra={};hr(Ra,{AI_AGENT_TOOL_NAMES:()=>On,AgentCore:()=>Hn,ApproveTokenTool:()=>qt,BaseNftMessageTool:()=>fe,BaseSwapService:()=>Ue,BaseTool:()=>v,BaseWalletActionTool:()=>le,BuyTokenTool:()=>Vt,ChatHistory:()=>Yt,DEFAULT_CHAIN:()=>ge,DEFAULT_PROVIDER:()=>on,DEFAULT_RPC_BY_CHAIN:()=>ie,DebridgeAdapter:()=>vt,EstimatePoolYieldTool:()=>_t,GeminiProvider:()=>se,GeminiSearchAiTool:()=>mt,HEX_TO_PANTOGRAPH:()=>Co,KnowledgeBase:()=>jt,MoralisService:()=>E,NFTContractInfoTool:()=>ut,NFTMetadataTool:()=>dt,NFT_AGENT_TOOL_NAMES:()=>Bn,OpenAddLiquidityFormTool:()=>At,POOL_AGENT_TOOL_NAMES:()=>qn,PantographService:()=>ee,PoolByAddressTool:()=>yt,PoolDetailTool:()=>gt,PoolFallbackAiTool:()=>pt,PoolSearchTool:()=>ft,PoolService:()=>Me,PreviewAddLiquidityTool:()=>Pt,RelayAdapter:()=>St,Router:()=>zt,SUPPORTED_CHAINS:()=>$e,SUPPORTED_CHAINS_LABEL:()=>Ne,SWAP_PROVIDER_CONFIG:()=>Cn,SendNativeTool:()=>Bt,SendNftTool:()=>Gt,SendTokenTool:()=>Ot,Subagent:()=>j,SubgraphAddPoolTool:()=>Dt,SubgraphCoinPoolPairsTool:()=>It,SubgraphPoolByAddressTool:()=>Rt,SubgraphPoolByPositionIdTool:()=>Et,SubgraphPoolSearchTool:()=>Ct,SubgraphPositionDetailTool:()=>Nt,SubgraphTrendingPoolsTool:()=>Ut,Summarizer:()=>Kt,SwapServiceFactory:()=>Le,SwapTokenTool:()=>Ht,Synthesizer:()=>Qt,TOKEN_AGENT_TOOL_NAMES:()=>Mn,TRANSFER_TOPIC:()=>Ko,TRENDING_DEFAULT_BY_HEX_CHAIN:()=>Uo,TokenAnalyticsTool:()=>je,TokenHoldersTool:()=>Ke,TokenInfoTool:()=>Ye,TokenScoreTool:()=>ze,ToolRegistry:()=>Fe,TopGainersTool:()=>Xe,TopPoolsTool:()=>ht,TransactionByHashTool:()=>lt,TrendingTokensTool:()=>Qe,UNISWAP_CHAIN_SLUG:()=>sn,UpstashKnowledgeBase:()=>Re,WALLET_ACTION_AGENT_TOOL_NAMES:()=>Fn,WALLET_AGENT_TOOL_NAMES:()=>Ln,WalletApprovalsTool:()=>rt,WalletDefiPositionsTool:()=>at,WalletDefiProtocolPositionsTool:()=>it,WalletDefiSummaryTool:()=>st,WalletHistoryTool:()=>Ze,WalletNFTsTool:()=>ct,WalletNetWorthTool:()=>kn,WalletNftTransfersTool:()=>tt,WalletPnlSummaryTool:()=>nt,WalletPnlTool:()=>ot,WalletTokenBalancesTool:()=>Je,WalletTokenTransfersTool:()=>et,ZERO_ADDRESS:()=>O,buildRangePresets:()=>ao,buildUniswapPoolUrl:()=>oe,clampTick:()=>ve,configureSupportedChains:()=>yn,createAiAgent:()=>dn,createDefaultSubagents:()=>$n,createNftAgent:()=>un,createPoolAgent:()=>mn,createTokenAgent:()=>cn,createWalletActionAgent:()=>pn,createWalletAgent:()=>ln,ethCallAt:()=>Ho,ethCallByChain:()=>so,getAffiliateFee:()=>kt,getChainMeta:()=>te,getDefaultRpcUrl:()=>Us,getGatewayAddress:()=>Rn,getNativeTokenInfo:()=>xt,getPositionManagerAddress:()=>En,getProviderByChain:()=>_n,ingestKnowledgeBase:()=>ir,priceToTick:()=>De,resolveRpcUrl:()=>Tt,roundTickToSpacing:()=>Te,rpcCall:()=>xe,setRpcOverrides:()=>ro,swapServiceFactory:()=>ye,tickToPrice:()=>ue,toPantographChain:()=>en});module.exports=fr(Ra);var po=gr(require("axios"));var mo="0.2.13";var br=`keyring-agent-core/${mo} (Keyring Chatbot Agent; +https://keyring.app)`,wr=3e4,kr=typeof process<"u"&&process.versions?.node!=null,Yn=class{instance;constructor(e={}){this.instance=po.default.create({baseURL:e.baseURL,timeout:e.timeout??wr,validateStatus:()=>!0,headers:{Accept:"application/json",...kr?{"User-Agent":e.userAgent??br}:{},...e.headers}})}async request(e,t,n,r={}){let o=await this.instance.request({...r,method:e,url:t,data:n}),s={};for(let[a,i]of Object.entries(o.headers??{}))i!=null&&(s[a.toLowerCase()]=String(i));return{ok:o.status>=200&&o.status<300,status:o.status,statusText:o.statusText,data:o.data,headers:s}}get(e,t){return this.request("GET",e,void 0,t)}post(e,t,n){return this.request("POST",e,t,n)}put(e,t,n){return this.request("PUT",e,t,n)}patch(e,t,n){return this.request("PATCH",e,t,n)}delete(e,t){return this.request("DELETE",e,void 0,t)}},_=new Yn;function N(c){let{data:e,status:t,statusText:n}=c,r=n?`HTTP ${t} ${n}`:`HTTP ${t}`;if(typeof e=="string")return e.trim()||r;if(e&&typeof e=="object"){let o=e,s=i=>i&&typeof i=="object"?i.message:void 0,a=[o.message,o.errorMessage,o.error,o.error_description,o.detail,s(o.error),s(o.data)];for(let i of a)if(typeof i=="string"&&i.trim())return i.trim()}return r}var ho={string:"STRING",number:"NUMBER",boolean:"BOOLEAN",object:"OBJECT",array:"ARRAY"},Tr="https://nft-demo.keyring.app/api/gemini-stable",vr=new Set([408,429,500,502,503,504]),go=3e4,gn=c=>new Promise(e=>setTimeout(e,c));function fn(c,e,t){if(t!=null&&t>0)return Math.min(t,go);let n=Math.min(e*2**(c-1),go);return n/2+Math.random()*(n/2)}function Sr(c){if(!c)return null;let e=Number(c);if(Number.isFinite(e))return Math.max(0,e*1e3);let t=Date.parse(c);return Number.isNaN(t)?null:Math.max(0,t-Date.now())}var se=class{model;maxTokens;temperature;baseUrl;constructor(e){this.model=e.model??"gemini-2.5-flash-lite",this.maxTokens=e.maxTokens??4096,this.temperature=e.temperature??1,this.baseUrl=e.baseUrl??Tr}async chat(e,t,n){let{systemInstruction:r,contents:o}=this.toContents(e),{maxRetries:s=5,retryDelayMs:a=1e3}=n??{},i=o.length>0?o:[{role:"user",parts:[{text:"(continue)"}]}];if(n?._debug){console.log("[GeminiProvider] turn sequence:");for(let m of i){let p=m.parts.map(h=>h.functionCall?`fc:${h.functionCall.name}`:h.functionResponse?`fr:${h.functionResponse.name}`:`text:${(h.text??"").slice(0,40)}`).join(", ");console.log(` ${m.role}: [${p}]`)}}let l={contents:i,generationConfig:{maxOutputTokens:this.maxTokens,temperature:this.temperature}};r&&(l.systemInstruction=r),t?.length?(l.tools=[{functionDeclarations:this.toFunctionDeclarations(t)}],n?.forceToolUse&&(l.toolConfig={functionCallingConfig:{mode:"ANY"}})):n?.googleSearch&&(l.tools=[{googleSearch:{}}]);let u=`${this.baseUrl}/v1beta/models/${this.model}:generateContent`,d="Unknown error";for(let m=1;m<=s;m++){let p;try{p=await _.post(u,l,{headers:{"Content-Type":"application/json"}})}catch(b){if(d=b instanceof Error?b.message:String(b),m>=s)break;await gn(fn(m,a));continue}if(!p.ok){let b=typeof p.data=="string"?p.data:JSON.stringify(p.data)||p.statusText;if(d=`Gemini proxy error ${p.status}: ${b}`,!vr.has(p.status))throw new Error(d);if(m>=s)break;let T=Sr(p.headers["retry-after"]??null);await gn(fn(m,a,T));continue}if(p.data==null||typeof p.data!="object"){if(d="Gemini proxy returned invalid JSON",m>=s)break;await gn(fn(m,a));continue}let h=p.data,g=h.candidates?.[0];if(!g)return{text:"",toolCalls:[]};let f="",y=[],k=g.content?.parts;if(!Array.isArray(k)||k.length===0){let b=g.finishReason;if(!(b==="SAFETY"||b==="RECITATION")&&m<s){d=`Gemini returned an empty turn (finishReason=${b??"none"})`,await gn(fn(m,a));continue}return{text:"",toolCalls:[]}}for(let b of k)b.text&&(f+=b.text),b.functionCall&&y.push({toolName:b.functionCall.name,args:b.functionCall.args??{},callId:b.functionCall.id??`call_${Date.now()}_${Math.random().toString(36).slice(2,8)}`});let w=h.usageMetadata?{promptTokens:h.usageMetadata.promptTokenCount??0,completionTokens:h.usageMetadata.candidatesTokenCount??0,totalTokens:h.usageMetadata.totalTokenCount??0}:void 0;return{text:f,toolCalls:y,usage:w}}throw new Error(d)}toContents(e){let t=[],n=[];for(let a of e){if(a.role==="system"){t.push(a.content);continue}if(a.role==="tool"){n.push({role:"function",parts:[{functionResponse:{name:a.toolName??"unknown",response:{result:a.content},...a.toolCallId?{id:a.toolCallId}:{}}}]});continue}if(a.role==="assistant"&&a.toolCalls?.length){n.push({role:"model",parts:a.toolCalls.map(i=>({functionCall:{name:i.toolName,args:i.args,...i.callId?{id:i.callId}:{}}}))});continue}n.push({role:a.role==="assistant"?"model":"user",parts:[{text:a.content}]})}let r=[];for(let a of n){let i=r[r.length-1],l=a.parts.some(d=>d.functionCall),u=i?.parts.some(d=>d.functionCall);i&&i.role===a.role&&!l&&!u?i.parts.push(...a.parts):r.push({...a,parts:[...a.parts]})}let o=[];for(let a=0;a<r.length;a++){let i=r[a],l=o[o.length-1];i.role==="function"&&!l?.parts.some(d=>d.functionCall)||i.role==="model"&&i.parts.some(d=>d.functionCall)&&!(r[a+1]?.role==="function")||o.push(i)}for(;o.length>0&&o[0].role!=="user";)o.shift();return{systemInstruction:t.length?{role:"user",parts:[{text:t.join(`
2
2
  `)}]}:null,contents:o}}toFunctionDeclarations(e){return e.map(t=>{let n={},r=[];for(let o of t.parameters){let s={type:ho[o.type]??"STRING",description:o.description};o.type==="array"&&(s.items={type:ho[o.items?.type??"string"]??"STRING"}),n[o.name]=s,o.required&&r.push(o.name)}return{name:t.name,description:t.description,parameters:{type:"OBJECT",properties:n,required:r}}})}};var Fe=class{tools=new Map;register(e){if(this.tools.has(e.name))throw new Error(`Tool "${e.name}" is already registered`);this.tools.set(e.name,e)}unregister(e){return this.tools.delete(e)}get(e){return this.tools.get(e)}has(e){return this.tools.has(e)}getDefinitions(){return Array.from(this.tools.values()).map(e=>({name:e.name,description:e.description,parameters:e.parameters,category:e.category,kind:e.kind}))}getDefinitionsByCategory(e){return this.getDefinitions().filter(t=>t.category===e)}async execute(e,t,n){let r=this.tools.get(e);if(!r)return{toolName:e,callId:`err_${Date.now()}`,success:!1,error:`Tool "${e}" not found in registry`,duration:0};let o=Date.now();try{return{...await r.execute(t,n),duration:Date.now()-o}}catch(s){return{toolName:e,callId:`err_${Date.now()}`,success:!1,error:s instanceof Error?s.message:String(s),duration:Date.now()-o}}}get size(){return this.tools.size}listNames(){return Array.from(this.tools.keys())}};var $e=["0x1","0xa","0x38","0x89","0x2105","0xa4b1","0xa86a","0xe708"],xr=new Set($e),Ee=[...$e],fo=new Set(Ee),ge="0x1",Ne="Ethereum, Optimism, BSC, Polygon, Base, Arbitrum, Avalanche, Linea";function Ar(c){return c.map(e=>Ve[e]??e).join(", ")}function yn(c){let e=new Set;if(c)for(let n of c){let r=Kn(n);r&&e.add(r)}let t=$e.filter(n=>!e.has(n));Ee=t.length>0?[...t]:[...$e],fo=new Set(Ee),ge=Ee.includes("0x1")?"0x1":Ee[0],Ne=Ar(Ee)}var Pr={"0x1":"0x1",1:"0x1",eth:"0x1",ether:"0x1",ethereum:"0x1","ethereum mainnet":"0x1",mainnet:"0x1","0xa":"0xa",10:"0xa",op:"0xa",optimism:"0xa","op mainnet":"0xa","optimism mainnet":"0xa","0x38":"0x38",56:"0x38",bsc:"0x38",bnb:"0x38",binance:"0x38","bnb chain":"0x38","bnb smart chain":"0x38","binance chain":"0x38","binance smart chain":"0x38","0x89":"0x89",137:"0x89",matic:"0x89",polygon:"0x89","polygon mainnet":"0x89","polygon pos":"0x89","0x2105":"0x2105",8453:"0x2105",base:"0x2105","base mainnet":"0x2105","0xa4b1":"0xa4b1",42161:"0xa4b1",arb:"0xa4b1",arbitrum:"0xa4b1","arbitrum one":"0xa4b1","arbitrum mainnet":"0xa4b1","0xa86a":"0xa86a",43114:"0xa86a",avax:"0xa86a",avalanche:"0xa86a","avalanche c-chain":"0xa86a","avalanche cchain":"0xa86a","avax c-chain":"0xa86a","0xe708":"0xe708",59144:"0xe708",linea:"0xe708","linea mainnet":"0xe708"};function he(c){let e=Kn(c);return e&&fo.has(e)?e:null}function Kn(c){if(typeof c!="string")return null;let e=c.trim().toLowerCase().replace(/\s+/g," ");if(!e)return null;let t=Pr[e];if(t)return t;let n=null;return/^0x[0-9a-f]+$/.test(e)?n=e:/^[0-9]+$/.test(e)&&(n="0x"+Number(e).toString(16)),n&&xr.has(n)?n:null}function bn(c){return he(c)!==null}var We=class extends Error{constructor(t){super(`Chain "${t}" is not supported. Supported chains: ${Ne}.`);this.requested=t;this.name="UnsupportedChainError"}requested;code="unsupported_chain"},_e=c=>typeof c=="string"&&c.trim()!=="";function yo(c,e){if(_e(c)){let t=he(c);if(t)return t;throw new We(c.trim())}if(_e(e?.chain)){let t=he(e.chain);if(t)return t;throw new We(e.chain.trim())}return null}function wn(c,e){return he(c)??he(e?.chain)??void 0}var Ve={"0x1":"Ethereum","0xa":"Optimism","0x38":"BSC","0x89":"Polygon","0x2105":"Base","0xa4b1":"Arbitrum","0xa86a":"Avalanche","0xe708":"Linea"};function I(c){let e=Kn(c);return e&&Ve[e]?Ve[e]:typeof c=="string"&&c.trim()?c.trim():"the requested chain"}var _r={bnb:"0x38",matic:"0x89",pol:"0x89",avax:"0xa86a"};function bo(c){if(typeof c!="string")return null;let e=c.trim().toLowerCase();return e?_r[e]??null:null}var Cr={"0x1":"ETH","0xa":"ETH","0x38":"BNB","0x89":"POL","0x2105":"ETH","0xa4b1":"ETH","0xa86a":"AVAX","0xe708":"ETH"};function wo(c){let e=he(c);return e?Cr[e]??null:null}function He(c,e){if(!_e(c))return null;let t=he(c),n=he(e?.chain);return!t||!n||t===n?null:{requested:t,connected:n,requestedLabel:Ve[t]??t,connectedLabel:Ve[n]??n}}function Ur(){return Ee.map(c=>{let e=Ve[c]??c;return{label:e,prompt:e}})}var K=class extends Error{code="needs_chain_selection";buttons;constructor(e){super("Wallet is connected but no chain is set \u2014 ask the user to pick one."),this.name="NeedsChainSelectionError",this.buttons=e??Ur()}},Xt=class extends Error{code="no_wallet_connected";constructor(){super("No wallet address available \u2014 ask the user to connect a wallet or supply one."),this.name="NoWalletConnectedError"}};function Y(c,e){if(_e(c))return c.trim();if(_e(e?.walletAddress))return e.walletAddress.trim();throw new Xt}function R(c,e){if(_e(c)){let t=he(c);if(t)return t;throw new We(c.trim())}if(_e(e?.chain)){let t=he(e.chain);if(t)return t;throw new We(e.chain.trim())}if(_e(e?.walletAddress))throw new K;return ge}var v=class{category;async execute(e,t){let n=`call_${Date.now()}_${Math.random().toString(36).slice(2,8)}`,r=Date.now();try{let o=await this.run(e,t),s=Rr(o);return{toolName:this.name,callId:n,success:!0,data:s.data,duration:Date.now()-r,...s.actionButtons?{actionButtons:s.actionButtons}:{}}}catch(o){return o instanceof K?{toolName:this.name,callId:n,success:!0,data:{_instructions:"The user has a connected wallet but no chain is set. Ask them which chain to use \u2014 the chain-picker buttons rendered below your reply let them choose. Keep the message short in the user's language and do NOT list the chain names yourself (the buttons already show them)."},duration:Date.now()-r,actionButtons:o.buttons}:o instanceof Xt?{toolName:this.name,callId:n,success:!0,data:{_instructions:"The user has not connected a wallet. In the user's language, tell them this action needs a wallet \u2014 they can either connect their wallet or include a wallet address (0x\u2026) directly in their question. Keep it short and friendly."},duration:Date.now()-r}:{toolName:this.name,callId:n,success:!1,error:o instanceof Error?o.message:String(o),duration:Date.now()-r}}}};function Rr(c){if(!c||typeof c!="object")return{data:c};let e=c,t=e.actionButtons;if(!Array.isArray(t)||t.length===0)return{data:c};let{actionButtons:n,...r}=e;return{data:r,actionButtons:t}}var ko=require("js-sha3");function B(c){return typeof c=="string"&&/^0x[0-9a-fA-F]{40}$/.test(c)}function Er(c){return new Uint8Array(ko.keccak256.arrayBuffer(c))}function Jt(c){let e=c.startsWith("0x")?c.slice(2):c,t=e.length%2?"0"+e:e,n=new Uint8Array(t.length/2);for(let r=0;r<n.length;r++)n[r]=parseInt(t.slice(r*2,r*2+2),16);return n}function To(c){return"0x"+Array.from(c).map(e=>e.toString(16).padStart(2,"0")).join("")}function vo(c){return new TextEncoder().encode(c)}function Ie(...c){let e=c.reduce((r,o)=>r+o.length,0),t=new Uint8Array(e),n=0;for(let r of c)t.set(r,n),n+=r.length;return t}function Nr(c,e=32){let t=new Uint8Array(e);return t.set(c,e-c.length),t}function Ir(c,e=32){let t=new Uint8Array(e);return t.set(c),t}function So(c){let e=((c%(1n<<256n)+(1n<<256n))%(1n<<256n)).toString(16).padStart(64,"0");return Jt(e)}function Zt(c){return So(BigInt(c))}function xo(c){return c==="uint"?"uint256":c==="int"?"int256":c.match(/^uint$/)?"uint256":c.match(/^int$/)?"int256":c.replace(/^uint(\[|$)/,"uint256$1").replace(/^int(\[|$)/,"int256$1")}function Ao(c){let e=xo(c.type);if(e==="tuple"||e.startsWith("tuple[")){let t=(c.components??[]).map(Ao).join(","),n=e.startsWith("tuple[")?e.slice(5):"";return`(${t})${n}`}return e}function Dr(c){let e=(c.inputs??[]).map(Ao).join(",");return`${c.name??""}(${e})`}function jn(c,e){let t=Br(c.type);if(t){let[r,o]=t;return Lr({...c,type:o},e,r)}if(c.type==="tuple")return Mr(c,e);if(c.type==="address"){let r=String(e),o=r.startsWith("0x")?r.slice(2):r;return{dynamic:!1,encoded:Nr(Jt(o.toLowerCase()))}}if(c.type==="bool"){let r=new Uint8Array(32);return r[31]=e?1:0,{dynamic:!1,encoded:r}}let n=xo(c.type);if(/^uint\d*$/.test(n))return{dynamic:!1,encoded:Zt(BigInt(e))};if(/^int\d*$/.test(n)){let r=BigInt(e);return r<0n&&(r=r+(1n<<256n)),{dynamic:!1,encoded:So(r)}}if(/^bytes(\d+)$/.test(n)){let r=parseInt(n.slice(5),10),o=typeof e=="string"?Jt(e):e;if(o.length!==r)throw new Error(`bytes${r} expects exactly ${r} bytes, got ${o.length}`);return{dynamic:!1,encoded:Ir(o)}}if(n==="bytes"){let r=typeof e=="string"?Jt(e):e,o=Math.ceil(r.length/32)*32,s=new Uint8Array(o);return s.set(r),{dynamic:!0,encoded:Ie(Zt(r.length),s)}}if(n==="string"){let r=typeof e=="string"?vo(e):e,o=Math.ceil(r.length/32)*32,s=new Uint8Array(o);return s.set(r),{dynamic:!0,encoded:Ie(Zt(r.length),s)}}throw new Error(`Unsupported ABI type: ${c.type}`)}function zn(c){let e=0;for(let{dynamic:o,encoded:s}of c)e+=o?32:s.length;let t=[],n=[],r=0;for(let{dynamic:o,encoded:s}of c)o?(t.push(Zt(e+r)),n.push(s),r+=s.length):t.push(s);return Ie(...t,...n)}function Lr(c,e,t){let n=t===null;if(!n&&e.length!==t)throw new Error(`Array length mismatch: expected ${t}, got ${e.length}`);let r=!1,o=[];for(let s of e){let a=jn(c,s);a.dynamic&&(r=!0),o.push(a)}if(n||r){let s=zn(o);if(n){let a=Zt(o.length);return{dynamic:!0,encoded:o.length>0?Ie(a,s):a}}return{dynamic:!0,encoded:s}}return{dynamic:!1,encoded:Ie(...o.map(s=>s.encoded))}}function Mr(c,e){let t=c.components??[],n=!1,r=[];for(let o=0;o<t.length;o++){let s=t[o],a=Array.isArray(e)?e[o]:e[s.name??""],i=jn(s,a);i.dynamic&&(n=!0),r.push(i)}return{dynamic:n,encoded:n?zn(r):Ie(...r.map(o=>o.encoded))}}function Br(c){let e=c.match(/^(.*)\[(\d+)?\]$/);return e?[e[2]?Number(e[2]):null,e[1]]:void 0}function Or(c,e){if(c.length!==e.length)throw new Error(`Expected ${c.length} values, got ${e.length}`);if(c.length===0)return"0x";let t=c.map((r,o)=>jn(r,e[o])),n=zn(t);return To(n)}function Ge({abi:c,functionName:e,args:t=[]}){let n=c.find(i=>(i.type==="function"||i.type===void 0)&&i.name===e);if(!n)throw new Error(`Function "${e}" not found in ABI`);let r=Dr(n),o=Er(vo(r)).slice(0,4),s=n.inputs??[],a=s.length===0||t.length===0?new Uint8Array(0):Jt(Or(s,t).slice(2));return To(Ie(o,a))}var qr="https://wallet-api.pantograph.app",Fr="0x0000000000000000000000000000000000000000",Co={"0x1":"ether","0xa":"optimism","0x38":"bsc","0x89":"matic","0x2105":"base","0xa4b1":"arbitrum","0xa86a":"avax","0xe708":"linea"},Uo={"0x1":["0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2","0x2260fac5e5542a773aa44fbcfedf7c193bc2c599"],"0xa":["0x4200000000000000000000000000000000000006","0x68f180fcce6836688e9084f035309e29bf0a2095","0xc47da4cb96ce65a96844a01bfae509f9d5454534"],"0x38":["0x2170ed0880ac9a755fd29b2688956bd959f933f8","0x7130d2a12b9bcbfae4f2634d864a1ee1ce3ead9c"],"0x89":["0x7ceb23fd6bc0add59e62ac25578270cff1b9f619","0x1bfd67037b42cf73acf2047067bd4f2c47d9bfd6"],"0x2105":["0x4200000000000000000000000000000000000006","0xcbb7c0000ab88b473b1f5afd9ef808440eed33bf","0xc47da4cb96ce65a96844a01bfae509f9d5454534"],"0xa4b1":["0x82af49447d8a07e3bd95bd0d56f35241523fbab1","0x2f2a2543b76a4166549f7aab2e75bef0aefc5b0f"],"0xa86a":["0x49d5c2bdffac6ce2bfdb6640f4f80f226bc10bab","0x0555e30da8f98308edb960aa94c0db47230d2b9c"],"0xe708":["0xe5d7c2a44ffddf6b295a15c148167daaaf5cf34f","0x3aab2285ddcddad8edf438c1bab47e1a9d05a9b4"]};function en(c){return Co[c.toLowerCase()]||c}function Po(c){let e=[];for(let[t,n]of Object.entries(c))if(n!=null)if(Array.isArray(n))for(let r of n)e.push(`${encodeURIComponent(t)}=${encodeURIComponent(String(r))}`);else e.push(`${encodeURIComponent(t)}=${encodeURIComponent(String(n))}`);return e.join("&")}function Qn(c){let e=c,t=e.decimals,n=typeof t=="number"?t:typeof t=="string"?parseInt(t,10):NaN,r=e.price,o=r!=null?parseFloat(String(r)):NaN;return{...e,token_address:e.token_address??e.address??"",decimals:Number.isFinite(n)?n:0,...Number.isFinite(o)?{usd_price:o}:{}}}function Z(c){if(c==null)return null;let e=typeof c=="number"?c:parseFloat(String(c));return Number.isFinite(e)?e:null}function _o(c,e){let t=c.decimals,n=typeof t=="number"?t:typeof t=="string"?parseInt(t,10):null,r=Z(c.price_change_percentage_24h)??Z(c.priceChange24h)??Z(c.usd_price_24hr_percent_change)??Z(c.price_change_24h)??Z(c.priceChange);return{chainId:e,tokenAddress:c.address??c.token_address??"",name:c.name??null,symbol:c.symbol??null,decimals:n!=null&&Number.isFinite(n)?n:null,logo:c.logoURI??c.icon_image??c.logo??null,usdPrice:Z(c.price)??Z(c.usd_price),marketCap:Z(c.market_cap)??Z(c.marketCap),totalVolume:{"24h":Z(c.total_volume)??Z(c.volume24h)},pricePercentChange:{"24h":r}}}var ee=class{baseUrl;constructor(e){this.baseUrl=(e?.baseUrl??qr).replace(/\/+$/,"")}async enrichTokenPrices(e,t){if(e.length!==0)try{let n=en(t),r=e.map(u=>u.token_address).join(","),o=`${this.baseUrl}/keyrings/tokens/${n}/v2?addresses=${encodeURIComponent(r)}`,s=await _.get(o);if(!s.ok)return;let a=s.data,i=Array.isArray(a)?a:Array.isArray(a.data)?a.data:[],l=new Map;for(let u of i)u.address&&l.set(u.address.toLowerCase(),{price:u.price!=null?parseFloat(String(u.price)):NaN,icon_image:u.icon_image});for(let u=0;u<e.length;u++){let d=l.get(e[u].token_address.toLowerCase());d&&(d.icon_image&&(e[u].logo=d.icon_image,e[u].thumbnail=d.icon_image),isNaN(d.price)||(e[u].usd_price=d.price,e[u].usd_value=parseFloat(e[u].balance_formatted||"0")*d.price))}}catch{}}async getTokenMetadata(e,t){try{let n=en(t),r=`${this.baseUrl}/keyrings/tokens/${n}/v2?addresses=${encodeURIComponent(e)}`,o=await _.get(r);if(!o.ok)return null;let s=o.data,a=Array.isArray(s)?s:Array.isArray(s.data)?s.data:[],i=e.toLowerCase()===Fr,l=a.find(u=>u.address?.toLowerCase()===e.toLowerCase()||i&&u.address===""||u.token_address?.toLowerCase()===e.toLowerCase()||i&&u.token_address==="");return l?Qn(l):null}catch{return null}}async getTokensMetadata(e,t){let n={};if(e.length===0)return n;try{let r=en(t),o=`${this.baseUrl}/keyrings/tokens/${r}/v2?addresses=${encodeURIComponent(e.join(","))}`,s=await _.get(o);if(!s.ok)return n;let a=s.data,i=Array.isArray(a)?a:Array.isArray(a.data)?a.data:[],l=new Set(e.map(u=>u.toLowerCase()));for(let u of i){let d=u.address?.toLowerCase()||u.token_address?.toLowerCase()||(u.address===""?"":null);d&&l.has(d)&&(n[d]=Qn(u))}}catch{}return n}async searchTokensByKey(e){let{key:t,chain:n}=e;if(!t)return{success:!1,error:"Search key is required"};let o=parseInt(n||ge,16);if(isNaN(o))return{success:!1,error:`Invalid hex chain: ${n}`};try{let s=Po({chainId:o,key:t}),a=`${this.baseUrl}/token-list?${s}`,i=await _.get(a);if(!i.ok)throw new Error(N(i));let l=i.data,u=[];return l?.data?u=Array.isArray(l.data)?l.data:Object.values(l.data).flat():typeof l=="object"&&!Array.isArray(l)&&(u=Object.values(l).flat()),{success:!0,data:u.map(Qn)}}catch(s){return{success:!1,error:s instanceof Error?s.message:"Unknown error"}}}async getTrendingTokens(e){let t=e?.chain||ge,n=5,r=e?.limit&&e.limit>0?Math.floor(e.limit):void 0;try{let o=Uo[t.toLowerCase()]??[],s=r!=null?r+o.length:n+o.length,[a,i]=await Promise.allSettled([this.fetchTopGainers(t,s),o.length>0?this.getTokensMetadata(o,t):Promise.resolve({})]),l=a.status==="fulfilled"?a.value:[],u=i.status==="fulfilled"?o.map(g=>i.value[g.toLowerCase()]).filter(g=>g!=null).map(g=>_o(g,t)):[],d=new Set,m=[];for(let g of u){let f=g.tokenAddress?.toLowerCase();!f||d.has(f)||(d.add(f),m.push(g))}let p=[];for(let g of l){let f=g.tokenAddress?.toLowerCase();!f||d.has(f)||(d.add(f),p.push(g))}let h=r!=null?[...m,...p].slice(0,r):[...m,...p.slice(0,n)];return await this.enrichTrendingMarketData(h,t),{success:!0,data:h}}catch(o){return{success:!1,error:o instanceof Error?o.message:"Unknown error"}}}async enrichTrendingMarketData(e,t){let n=e.filter(o=>o.tokenAddress&&(o.marketCap==null||o.totalVolume?.["24h"]==null));if(n.length===0)return;let r=await this.getTokensMetadata(n.map(o=>o.tokenAddress),t);for(let o of n){let s=r[o.tokenAddress.toLowerCase()];s&&(o.marketCap==null&&(o.marketCap=Z(s.market_cap)??Z(s.marketCap)),o.totalVolume?.["24h"]==null&&(o.totalVolume={"24h":Z(s.total_volume)??Z(s.volume24h)}))}}async fetchTopGainers(e,t,n="24h"){let r=en(e),o=Po({duration:n,limit:t}),s=`${this.baseUrl}/token-list/top-gainers/${r}${o?`?${o}`:""}`,a=[];for(let i=0;i<5;i++){let l=await _.get(s);if(!l.ok)throw new Error(N(l));let u=l.data;if(a=Array.isArray(u)?u:Array.isArray(u.data)?u.data:[],a.length>0)break;i<4&&await new Promise(d=>setTimeout(d,2e3))}return a.map(i=>_o(i,e))}async getTopGainers(e){let t=e?.chain||ge,n=e?.limit&&e.limit>0?Math.floor(e.limit):void 0,r=e?.duration||"24h";try{let o=await this.fetchTopGainers(t,n,r);return await this.enrichTrendingMarketData(o,t),{success:!0,data:o}}catch(o){return{success:!1,error:o instanceof Error?o.message:"Unknown error"}}}};var $r="https://nft.keyring.app",Wr="https://nft-demo.keyring.app",Vr=.01,U=5,F=1e3,Hr="0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",Gr="0x0000000000000000000000000000000000000000";function V(c){return c||ge}function $(c){let e=[];for(let[t,n]of Object.entries(c))if(n!=null)if(Array.isArray(n))for(let r of n)e.push(`${encodeURIComponent(t)}=${encodeURIComponent(String(r))}`);else e.push(`${encodeURIComponent(t)}=${encodeURIComponent(String(n))}`);return e.join("&")}function W(c){return new Promise(e=>setTimeout(e,c))}function Yr(c){if(Array.isArray(c))return c;if(c&&typeof c=="object"){let e=c;return Array.isArray(e.result)?e.result.map(t=>({protocol_id:t.protocolId,protocol_name:t.protocolName,protocol_url:t.protocolUrl??"",protocol_logo:t.protocolLogo??"",position:{label:t.position.label,address:t.position.address,balance_usd:t.position.balanceUsd??0,total_unclaimed_usd_value:t.position.unclaimedUsd??0,position_details:t.position.details,tokens:t.position.tokens.map(n=>({token_type:n.tokenType,address:n.address,contract_address:n.address,name:n.name??"",symbol:n.symbol??"",decimals:n.decimals??18,logo:n.logo,balance:n.balance??"0",balance_formatted:n.balanceFormatted??"0",usd_price:n.usdPrice,usd_value:n.usdValue}))}})):Object.keys(e).filter(t=>/^\d+$/.test(t)).sort((t,n)=>Number(t)-Number(n)).map(t=>e[t]).filter(t=>!!t&&typeof t=="object")}return[]}function Xn(c){if(Array.isArray(c))return c;if(c&&typeof c=="object"){let e=c;return Array.isArray(e.result)?e.result:Object.keys(e).filter(t=>/^\d+$/.test(t)).sort((t,n)=>Number(t)-Number(n)).map(t=>e[t]).filter(t=>!!t&&typeof t=="object")}return[]}var E=class{baseUrl;v1BaseUrl;pantograph;constructor(e){this.baseUrl=(e?.baseUrl??$r).replace(/\/+$/,""),this.v1BaseUrl=(e?.v1BaseUrl??Wr).replace(/\/+$/,""),this.pantograph=new ee({baseUrl:e?.pantographUrl})}async getWalletTokenBalances(e){let{address:t,chain:n,tokenAddresses:r,excludeSpam:o=!1,excludeUnverifiedContracts:s=!1}=e;if(!t)return{success:!1,error:"Address is required"};let a=V(n),i="Unknown error";for(let l=1;l<=U;l++)try{let u={chain:a,exclude_spam:o,exclude_unverified_contracts:s,limit:100};r&&(u.token_addresses=r);let d=[],m=null;do{m&&(u.cursor=m);let h=$(u),g=`${this.baseUrl}/api/moralis/proxy/wallets/${encodeURIComponent(t)}/tokens?${h}`,f=await _.get(g);if(!f.ok)throw new Error(N(f));let y=f.data,k=y?.data??y;k?.result?.length>0&&d.push(...k.result),m=k?.cursor??null}while(m);let p={result:d};return p?.result?.length>0&&(p.result=p.result.filter(h=>parseFloat(h.balance||"0")>0),p.result=p.result.map(h=>({...h,token_address:h.token_address.toLowerCase()===Hr?Gr:h.token_address})),await this.enrichTokenPrices(p.result,a),p.result=p.result.filter(h=>h.usd_value==null||h.usd_value>=Vr)),{success:!0,data:p}}catch(u){i=u instanceof Error?u.message:"Unknown error",l<U&&await W(F)}return{success:!1,error:i}}async getTokenMetadata(e){let{address:t,chain:n}=e;if(!t)return{success:!1,error:"Token address is required"};let r=V(n),o="Unknown error",s=await this.pantograph.getTokenMetadata(t,r);if(s)return{success:!0,data:s};for(let a=1;a<=U;a++)try{let[i,l]=await Promise.allSettled([_.get(`${this.baseUrl}/api/moralis/proxy/erc20/metadata?${$({chain:r,addresses:[t]})}`),_.get(`${this.baseUrl}/api/moralis/proxy/erc20/${encodeURIComponent(t)}/price?${$({chain:r,include:"percent_change"})}`)]),u=null;if(i.status==="fulfilled"&&i.value.ok){let m=i.value.data;u=(Array.isArray(m)?m:Array.isArray(m?.data)?m.data:[])[0]??null}if(!u){o="Token metadata not found",a<U&&await W(F);continue}let d=null;if(l.status==="fulfilled"&&l.value.ok){let m=l.value.data;d=m?.data??m}return{success:!0,data:{...u,...d?.usd_price?{usd_price:d.usd_price}:{},...d?.usd_price_change_percentage_24h?{usd_price_change_percentage_24h:d.usd_price_change_percentage_24h}:{}}}}catch(i){o=i instanceof Error?i.message:"Unknown error",a<U&&await W(F)}return{success:!1,error:o}}async getWalletNFTs(e){let{address:t,chain:n,limit:r=10,excludeSpam:o=!0,cursor:s,tokenAddresses:a,includePrices:i,format:l="decimal"}=e;if(!t)return{success:!1,error:"Address is required"};let u=V(n),d="Unknown error";for(let m=1;m<=U;m++)try{let p={chain:u,format:l,limit:r,exclude_spam:o,media_items:!0,normalizeMetadata:!0};s&&(p.cursor=s),i&&(p.include_prices=!0),a&&a.length>0&&(p.token_addresses=a);let h=$(p),g=`${this.baseUrl}/api/moralis/proxy/${encodeURIComponent(t)}/nft?${h}`,f=await _.get(g);if(!f.ok)throw new Error(N(f));let y=f.data;return{success:!0,data:y?.data??y}}catch(p){d=p instanceof Error?p.message:"Unknown error",m<U&&await W(F)}return{success:!1,error:d}}async getNftMetadata(e){let{address:t,tokenId:n,chain:r,format:o="decimal",normalizeMetadata:s=!0,mediaItems:a=!0,include:i}=e;if(!t)return{success:!1,error:"NFT contract address is required"};if(n==null||n==="")return{success:!1,error:"Token ID is required"};if(!B(t))return{success:!1,error:"NFT metadata lookup by token ID is only supported on EVM chains"};let l=V(r),u="Unknown error";for(let d=1;d<=U;d++)try{let m={chain:l,format:o,normalizeMetadata:s,media_items:a};i&&(m.include=i);let p=$(m),h=`${this.baseUrl}/api/moralis/proxy/nft/${encodeURIComponent(t)}/${encodeURIComponent(n)}?${p}`,g=await _.get(h);if(!g.ok)throw new Error(N(g));let f=g.data;return{success:!0,data:f?.data??f}}catch(m){u=m instanceof Error?m.message:"Unknown error",d<U&&await W(F)}return{success:!1,error:u}}async getNftContractMetadata(e){let{address:t,chain:n}=e;if(!t)return{success:!1,error:"Contract address is required"};let r=V(n),o="Unknown error";for(let s=1;s<=U;s++)try{let a=$({chain:r}),i=`${this.baseUrl}/api/moralis/proxy/nft/${encodeURIComponent(t)}/metadata?${a}`,l=await _.get(i);if(!l.ok)throw new Error(N(l));let u=l.data;return{success:!0,data:u?.data??u}}catch(a){o=a instanceof Error?a.message:"Unknown error",s<U&&await W(F)}return{success:!1,error:o}}async getWalletHistory(e){let{address:t,chain:n,limit:r=25,fromDate:o,toDate:s,fromBlock:a,toBlock:i,cursor:l,order:u="DESC",includeInternalTransactions:d,nftMetadata:m}=e;if(!t)return{success:!1,error:"Address is required"};if(!B(t))return{success:!1,error:"Wallet history is only supported for EVM addresses"};let p=V(n),h=Math.max(1,Math.min(100,Math.floor(r))),g="Unknown error";for(let f=1;f<=U;f++)try{let y={chain:p,limit:h,order:u};o&&(y.from_date=o),s&&(y.to_date=s),a!=null&&(y.from_block=a),i!=null&&(y.to_block=i),l&&(y.cursor=l),d!=null&&(y.include_internal_transactions=d),m!=null&&(y.nft_metadata=m);let k=$(y),w=`${this.baseUrl}/api/moralis/proxy/wallets/${encodeURIComponent(t)}/history?${k}`,b=await _.get(w);if(!b.ok)throw new Error(N(b));let T=b.data;return{success:!0,data:T?.data??T}}catch(y){g=y instanceof Error?y.message:"Unknown error",f<U&&await W(F)}return{success:!1,error:g}}async getWalletTokenTransfers(e){let{address:t,chain:n,contractAddresses:r,limit:o=25,fromDate:s,toDate:a,fromBlock:i,toBlock:l,cursor:u,order:d="DESC"}=e;if(!t)return{success:!1,error:"Address is required"};if(!B(t))return{success:!1,error:"ERC-20 transfers are only supported for EVM addresses"};let m=V(n),p=Math.max(1,Math.min(100,Math.floor(o))),h="Unknown error";for(let g=1;g<=U;g++)try{let f={chain:m,limit:p,order:d};s&&(f.from_date=s),a&&(f.to_date=a),i!=null&&(f.from_block=i),l!=null&&(f.to_block=l),u&&(f.cursor=u),r?.length&&(f.contract_addresses=r);let y=$(f),k=`${this.baseUrl}/api/moralis/proxy/${encodeURIComponent(t)}/erc20/transfers?${y}`,w=await _.get(k);if(!w.ok)throw new Error(N(w));let b=w.data;return{success:!0,data:b?.data??b}}catch(f){h=f instanceof Error?f.message:"Unknown error",g<U&&await W(F)}return{success:!1,error:h}}async getWalletNftTransfers(e){let{address:t,chain:n,contractAddresses:r,limit:o=25,fromDate:s,toDate:a,fromBlock:i,toBlock:l,cursor:u,order:d="DESC",includePrices:m,format:p="decimal"}=e;if(!t)return{success:!1,error:"Address is required"};if(!B(t))return{success:!1,error:"NFT transfers are only supported for EVM addresses"};let h=V(n),g=Math.max(1,Math.min(100,Math.floor(o))),f="Unknown error";for(let y=1;y<=U;y++)try{let k={chain:h,limit:g,order:d,format:p};s&&(k.from_date=s),a&&(k.to_date=a),i!=null&&(k.from_block=i),l!=null&&(k.to_block=l),u&&(k.cursor=u),r?.length&&(k.contract_addresses=r),m!=null&&(k.include_prices=m);let w=$(k),b=`${this.baseUrl}/api/moralis/proxy/${encodeURIComponent(t)}/nft/transfers?${w}`,T=await _.get(b);if(!T.ok)throw new Error(N(T));let P=T.data;return{success:!0,data:P?.data??P}}catch(k){f=k instanceof Error?k.message:"Unknown error",y<U&&await W(F)}return{success:!1,error:f}}async getTokenHolders(e){let{address:t,chain:n}=e;if(!t)return{success:!1,error:"Contract address is required"};if(!B(t))return{success:!1,error:"Token holders is only supported for EVM contract addresses"};let r=V(n),o="Unknown error";for(let s=1;s<=U;s++)try{let a=$({chain:r}),i=`${this.baseUrl}/api/moralis/proxy/erc20/${encodeURIComponent(t)}/holders?${a}`,l=await _.get(i);if(!l.ok)throw new Error(N(l));let u=l.data;return{success:!0,data:u?.data??u}}catch(a){o=a instanceof Error?a.message:"Unknown error",s<U&&await W(F)}return{success:!1,error:o}}async getWalletNetWorth(e){let{address:t,chains:n,excludeSpam:r,excludeUnverifiedContracts:o,maxTokenInactivity:s,minPairSideLiquidityUsd:a}=e;if(!t)return{success:!1,error:"Address is required"};if(!B(t))return{success:!1,error:"Wallet net worth is only supported for EVM addresses"};let i="Unknown error";for(let l=1;l<=U;l++)try{let u={};n&&n.length>0&&(u.chains=n.map(y=>V(y))),r!=null&&(u.exclude_spam=r),o!=null&&(u.exclude_unverified_contracts=o),s!=null&&(u.max_token_inactivity=s),a!=null&&(u.min_pair_side_liquidity_usd=a);let d=$(u),m=`${this.baseUrl}/api/moralis/proxy/wallets/${encodeURIComponent(t)}/net-worth${d?`?${d}`:""}`,p=await _.get(m);if(!p.ok)throw new Error(N(p));let h=p.data;return{success:!0,data:h?.data??h}}catch(u){i=u instanceof Error?u.message:"Unknown error",l<U&&await W(F)}return{success:!1,error:i}}async getWalletPnlSummary(e){let{address:t,chain:n,days:r}=e;if(!t)return{success:!1,error:"Address is required"};if(!B(t))return{success:!1,error:"Wallet PnL summary is only supported for EVM addresses"};let o=V(n),s="Unknown error";for(let a=1;a<=U;a++)try{let i={chain:o};r&&(i.days=r);let l=$(i),u=`${this.baseUrl}/api/moralis/proxy/wallets/${encodeURIComponent(t)}/profitability/summary?${l}`,d=await _.get(u);if(!d.ok)throw new Error(N(d));let m=d.data;return{success:!0,data:m?.data??m}}catch(i){s=i instanceof Error?i.message:"Unknown error",a<U&&await W(F)}return{success:!1,error:s}}async getWalletPnl(e){let{address:t,chain:n,days:r,tokenAddresses:o}=e;if(!t)return{success:!1,error:"Address is required"};if(!B(t))return{success:!1,error:"Wallet PnL is only supported for EVM addresses"};let s=V(n),a="Unknown error";for(let i=1;i<=U;i++)try{let l={chain:s};r&&(l.days=r),o&&o.length>0&&(l.token_addresses=o.slice(0,25));let u=$(l),d=`${this.baseUrl}/api/moralis/proxy/wallets/${encodeURIComponent(t)}/profitability?${u}`,m=await _.get(d);if(!m.ok)throw new Error(N(m));let p=m.data;return{success:!0,data:p?.data??p}}catch(l){a=l instanceof Error?l.message:"Unknown error",i<U&&await W(F)}return{success:!1,error:a}}async getTransactionByHash(e){let{transactionHash:t,chain:n}=e;if(!t)return{success:!1,error:"Transaction hash is required"};let r=V(n),o="Unknown error";for(let s=1;s<=U;s++)try{let a=$({chain:r}),i=`${this.baseUrl}/api/moralis/proxy/transaction/${encodeURIComponent(t)}/verbose?${a}`,l=await _.get(i);if(!l.ok)throw new Error(N(l));let u=l.data;return{success:!0,data:u?.data??u}}catch(a){o=a instanceof Error?a.message:"Unknown error",s<U&&await W(F)}return{success:!1,error:o}}async getWalletApprovals(e){let{address:t,chain:n,limit:r,cursor:o}=e;if(!t)return{success:!1,error:"Address is required"};if(!B(t))return{success:!1,error:"Wallet approvals are only supported for EVM addresses"};let s=V(n),a="Unknown error";for(let i=1;i<=U;i++)try{let l={chain:s};r!=null&&(l.limit=r),o&&(l.cursor=o);let u=$(l),d=`${this.baseUrl}/api/moralis/proxy/wallets/${encodeURIComponent(t)}/approvals?${u}`,m=await _.get(d);if(!m.ok)throw new Error(N(m));let p=m.data;return{success:!0,data:p?.data??p}}catch(l){a=l instanceof Error?l.message:"Unknown error",i<U&&await W(F)}return{success:!1,error:a}}async getWalletDefiSummary(e){let{address:t,chain:n}=e;if(!t)return{success:!1,error:"Address is required"};if(!B(t))return{success:!1,error:"Wallet DeFi summary is only supported for EVM addresses"};let r=V(n),o="Unknown error";for(let s=1;s<=U;s++)try{let a=$({chains:r}),i=`${this.v1BaseUrl}/api/moralis-v1/proxy/wallets/${encodeURIComponent(t)}/defi/summary?${a}`,l=await _.get(i);if(!l.ok)throw new Error(N(l));let u=l.data;return{success:!0,data:u?.data??u}}catch(a){o=a instanceof Error?a.message:"Unknown error",s<U&&await W(F)}return{success:!1,error:o}}async getWalletDefiPositions(e){let{address:t,chain:n}=e;if(!t)return{success:!1,error:"Address is required"};if(!B(t))return{success:!1,error:"Wallet DeFi positions are only supported for EVM addresses"};let r=V(n),o="Unknown error";for(let s=1;s<=U;s++)try{let a=$({chains:r}),i=`${this.v1BaseUrl}/api/moralis-v1/proxy/wallets/${encodeURIComponent(t)}/defi/positions?${a}`,l=await _.get(i);if(!l.ok)throw new Error(N(l));let u=l.data,d=u?.data??u;return{success:!0,data:Yr(d)}}catch(a){o=a instanceof Error?a.message:"Unknown error",s<U&&await W(F)}return{success:!1,error:o}}async getWalletDefiProtocolPositions(e){let{address:t,protocol:n,chain:r}=e;if(!t)return{success:!1,error:"Address is required"};if(!n)return{success:!1,error:"Protocol identifier is required"};if(!B(t))return{success:!1,error:"Wallet DeFi protocol positions are only supported for EVM addresses"};let o=V(r),s="Unknown error";for(let a=1;a<=U;a++)try{let i=$({chains:o}),l=`${this.v1BaseUrl}/api/moralis-v1/proxy/wallets/${encodeURIComponent(t)}/defi/${encodeURIComponent(n)}/positions?${i}`,u=await _.get(l);if(!u.ok)throw new Error(N(u));let p=u.data.result;return{success:!0,data:{protocol_id:p.protocolId,protocol_name:p.protocolName,protocol_url:p.protocolUrl??"",protocol_logo:p.protocolLogo??"",total_usd_value:p.totalUsd??0,total_unclaimed_usd_value:p.totalUnclaimedUsd??null,positions:p.positions.map(g=>({label:g.label,address:g.address,balance_usd:g.balanceUsd??0,total_unclaimed_usd_value:g.unclaimedUsd??0,position_details:g.details,tokens:g.tokens.map(f=>({token_type:f.tokenType,address:f.address,contract_address:f.address,name:f.name??"",symbol:f.symbol??"",decimals:f.decimals??18,logo:f.logo,balance:f.balance??"0",balance_formatted:f.balanceFormatted??"0",usd_price:f.usdPrice,usd_value:f.usdValue}))}))}}}catch(i){s=i instanceof Error?i.message:"Unknown error",a<U&&await W(F)}return{success:!1,error:s}}async searchTokensByKey(e){return this.pantograph.searchTokensByKey(e)}async getTokenAnalytics(e){let{address:t,chain:n}=e;if(!t)return{success:!1,error:"Token contract address is required"};if(!B(t))return{success:!1,error:"Token analytics is only supported for EVM contract addresses"};let r=V(n),o="Unknown error";for(let s=1;s<=U;s++)try{let a=$({chain:r}),i=`${this.baseUrl}/api/moralis/proxy/tokens/${encodeURIComponent(t)}/analytics?${a}`,l=await _.get(i);if(!l.ok)throw new Error(N(l));let u=l.data;return{success:!0,data:u?.data??u}}catch(a){o=a instanceof Error?a.message:"Unknown error",s<U&&await W(F)}return{success:!1,error:o}}async getTokenScore(e){let{address:t,chain:n}=e;if(!t)return{success:!1,error:"Token contract address is required"};if(!B(t))return{success:!1,error:"Token score is only supported for EVM contract addresses"};let r=V(n),o="Unknown error";for(let s=1;s<=U;s++)try{let a=$({chain:r}),i=`${this.baseUrl}/api/moralis/proxy/tokens/${encodeURIComponent(t)}/score?${a}`,l=await _.get(i);if(!l.ok)throw new Error(N(l));let u=l.data;return{success:!0,data:u?.data??u}}catch(a){o=a instanceof Error?a.message:"Unknown error",s<U&&await W(F)}return{success:!1,error:o}}async getTrendingTokens(e){let t=e?.chain,n=e?.limit,r="Unknown error";for(let o=1;o<=U;o++)try{let s={};t&&(s.chain=V(t)),n!=null&&(s.limit=n);let a=$(s),i=`${this.baseUrl}/api/moralis/proxy/tokens/trending${a?`?${a}`:""}`,l=await _.get(i);if(!l.ok)throw new Error(N(l));let u=l.data,d=u?.data??u;return{success:!0,data:Xn(d)}}catch(s){r=s instanceof Error?s.message:"Unknown error",o<U&&await W(F)}return{success:!1,error:r}}async getTopGainers(e){let{chain:t,timeFrame:n,minMarketCap:r,securityScore:o}=e??{},s="Unknown error";for(let a=1;a<=U;a++)try{let i={};t&&(i.chain=V(t)),n&&(i.time_frame=n),r!=null&&(i.min_market_cap=r),o!=null&&(i.security_score=o);let l=$(i),u=`${this.baseUrl}/api/moralis/proxy/discovery/tokens/top-gainers${l?`?${l}`:""}`,d=await _.get(u);if(!d.ok)throw new Error(N(d));let m=d.data,p=m?.data??m;return{success:!0,data:Xn(p)}}catch(i){s=i instanceof Error?i.message:"Unknown error",a<U&&await W(F)}return{success:!1,error:s}}async getTopLosers(e){let{chain:t,timeFrame:n,minMarketCap:r,securityScore:o}=e??{},s="Unknown error";for(let a=1;a<=U;a++)try{let i={};t&&(i.chain=V(t)),n&&(i.time_frame=n),r!=null&&(i.min_market_cap=r),o!=null&&(i.security_score=o);let l=$(i),u=`${this.baseUrl}/api/moralis/proxy/discovery/tokens/top-losers${l?`?${l}`:""}`,d=await _.get(u);if(!d.ok)throw new Error(N(d));let m=d.data,p=m?.data??m;return{success:!0,data:Xn(p)}}catch(i){s=i instanceof Error?i.message:"Unknown error",a<U&&await W(F)}return{success:!1,error:s}}async enrichTokenPrices(e,t){return this.pantograph.enrichTokenPrices(e,t)}};var Ye=class extends v{name="get-token-info";description='Get information about a specific cryptocurrency token: price, metadata, 24h change, market data, and user holdings. Use this when the user asks about: token price, token info, "what is X token", "tell me about X", "how much is X worth", or any token-specific question. Supports: Ethereum, Optimism, BSC, Polygon, Base, Arbitrum, Avalanche, Linea';category="blockchain-data";parameters=[{name:"keyword",type:"string",description:'Token symbol, name, or contract address (e.g. "USDC", "Pepe", "ETH", "0x1234\u2026"). If a 0x address is provided, performs a precise contract lookup. Otherwise searches by name/symbol.',required:!0},{name:"chain",type:"string",description:'Hex chain ID: "0x1" Ethereum, "0xa" Optimism, "0x38" BSC, "0x89" Polygon, "0x2105" Base, "0xa4b1" Arbitrum, "0xa86a" Avalanche, "0xe708" Linea. ONLY set this when the user EXPLICITLY names a chain; otherwise OMIT it so the connected wallet chain is used.',required:!1},{name:"prompt",type:"string",description:`A rich, multi-angle research prompt sent to Gemini with Google Search grounding (real-time web access). The prompt must be self-contained \u2014 Gemini does NOT see the user's original message, only this prompt. ALWAYS include: the token symbol/name, the chain, and an instruction to use web search for up-to-date info. When the user asks an open/broad question (e.g. "show me X", "tell me about X", "what is X"), expand the prompt to cover ALL of these angles in one request: (1) what the token/project is and its core utility, (2) current USD price, 24h change, market cap, FDV, 24h volume, (3) recent news, announcements, or notable events in the last 7-30 days, (4) on-chain activity signals (holders, liquidity, unusual flows), (5) team/protocol updates or roadmap items, (6) key risks, red flags, or controversies, (7) sentiment and notable community discussion. When the user asks a narrow question (e.g. "price of X"), still include 2-3 supporting angles for context. Aim for 3-6 sentences. Be specific to the token. Do not use generic filler. Example for "show me JPYT": "Research the JPYT token on Ethereum using up-to-date web sources. Cover: what JPYT is and who issues it; current USD price, 24h change, market cap, FDV, and 24h volume; recent news or announcements in the last 30 days; on-chain signals such as holder count, liquidity, and unusual volume patterns; team or protocol updates; key risks or red flags; and overall sentiment."`,required:!0},{name:"buy_button_label",type:"string",description:`Label for the "Buy" suggestion button shown under the token info, IN THE USER'S CURRENT LANGUAGE. Use the exact placeholder "{token}" (do not translate the braces) for the token symbol; put the localized verb directly in the text. English example: "Buy {token}". Vietnamese example: "Mua {token}". Japanese example: "{token} \u8CFC\u5165". Always include {token}.`,required:!0},{name:"buy_prompt_template",type:"string",description:`A short "buy" command IN THE USER'S CURRENT LANGUAGE, submitted as the next user turn when the Buy button is clicked. Use the exact placeholder "{token}" (do not translate the braces) for the token symbol; put the localized verb directly in the text. English example: "buy {token}". Vietnamese example: "mua {token}". Japanese example: "{token} \u8CFC\u5165". Always include {token}.`,required:!0}];service;llm;constructor(e){super(),this.service=new E(e),this.llm=new se({})}async run(e,t){let n=e.keyword||"",r=R(e.chain,t),o=e.prompt||"",s=e.buy_button_label||"",a=e.buy_prompt_template||"",i=t?.walletAddress||void 0;return n?await this.searchToken(n,r,o,s,a,i):{error:"Missing required parameter: keyword"}}async execute(e,t){let n=await super.execute(e,t),r=n.data;return n.success&&r&&Array.isArray(r.actionButtons)&&r.actionButtons.length>0&&(n.actionButtons=r.actionButtons),n}async searchToken(e,t,n,r,o,s){let a=[s?`User's connected wallet address: ${s}`:null,t?`Current chain (hex chain ID): ${t}, only consider tokens on this chain`:null,"Use this context to personalize your answer where relevant (e.g. whether the user holds the token, chain-specific data)."].filter(Boolean).join(`
3
3
  `),[i,l,u]=await Promise.all([this.service.searchTokensByKey({key:e,chain:t}),s?this.findTokenInWallet(s,t,e):Promise.resolve(null),n?this.askGeminiWithSearch(n,a):Promise.resolve(null)]),d=i.success&&i.data?Array.isArray(i.data)?i.data:[]:[],m=e.toLowerCase(),p=b=>b.symbol?.toLowerCase()===m||b.token_address?.toLowerCase()===m,h=[...d].sort((b,T)=>(p(b)?0:1)-(p(T)?0:1)),g=null,f=h[0];if(f?.token_address){let b=await this.service.getTokenMetadata({address:f.token_address,chain:t});b.success&&b.data&&(g=b.data)}!g&&f&&(g={...f});let y=g?.symbol?.trim(),k=y?[{label:this.fillTemplate(r,y,"Buy {token}"),prompt:this.fillTemplate(o,y,"buy {token}")}]:[];return{_instructions:'Present the token info in a clear summary. Include: token name & symbol, contract address (shortened), current USD price, 24h price change % (prefix "+" for positive, "-" for negative), market cap, fully diluted valuation, 24h trading volume, liquidity, and holder count (if available). If the user holds this token, prominently show their balance and USD value. If research is present, weave its substance naturally into your answer \u2014 fold the relevant points into the description, context, and any notable news or risks. Do NOT add a labelled "AI Analysis" section or any similar heading, and do NOT mention that the analysis came from AI; just write it as part of the token summary. Format numbers in a human-readable way (e.g. $1.23, +5.67%, $12.5M). If a field is null, omit it. A "Buy" button is shown below your reply automatically \u2014 do NOT mention it, list it, or repeat its text.',chain:t,userHoldsToken:!!l,walletBalance:l,token:g,...d.length>1?{searchResults:h.slice(0,10)}:{},research:u,...k.length>0?{actionButtons:k}:{}}}fillTemplate(e,t,n){return(e&&e.includes("{token}")?e:n).replace(/\{token\}/g,t).replace(/\s+/g," ").trim()}async askGeminiWithSearch(e,t){let n=Date.now();try{return(await this.llm.chat([...t?[{role:"system",content:t,timestamp:n}]:[],{role:"user",content:e,timestamp:n}],void 0,{googleSearch:!0,maxRetries:1})).text?.trim()||null}catch{return null}}async findTokenInWallet(e,t,n){if(!n)return null;let r=await this.service.getWalletTokenBalances({address:e,chain:t,excludeSpam:!0,excludeUnverifiedContracts:!0});if(!r.success||!r.data?.result)return null;let o=n.toLowerCase(),s=r.data.result.find(a=>a.token_address.toLowerCase()===o||a.symbol?.toLowerCase()===o||a.name?.toLowerCase().includes(o));return s||null}};var Ke=class extends v{name="get-token-holders";description='Get holder statistics and analytics for an ERC-20 token. Returns: total holder count, holder count changes over time (5min/1h/6h/24h/3d/7d/30d), how holders acquired the token (swap/transfer/airdrop), supply concentration (% held by top 10/25/50/100/250/500 holders), and holder size distribution (whales, sharks, dolphins, fish, octopus, crabs, shrimps). Use tokenSymbol (e.g. "USDC") to look up by name \u2014 the tool auto-resolves the contract address. Use contractAddress when you already have the exact 0x address. Use this tool for questions like: "How many holders does USDC have?" (tokenSymbol=USDC), "Is USDC gaining or losing holders?" (tokenSymbol=USDC \u2192 holderChange), "How many new holders did ETH get in the last 7 days?" (tokenSymbol=WETH), "How concentrated is USDC ownership?" (tokenSymbol=USDC \u2192 holderSupply), "What % of supply do the top 10 holders control?" (tokenSymbol=USDC), "How many whales hold USDT?" (tokenSymbol=USDT \u2192 holderDistribution), "Did most holders buy via swap or transfer?" (tokenSymbol=USDC \u2192 holdersByAcquisition). Supports: Ethereum, Optimism, BSC, Polygon, Base, Arbitrum, Avalanche, Linea. ';category="blockchain-data";parameters=[{name:"tokenSymbol",type:"string",description:`Token symbol (e.g. "USDC", "USDT", "WETH"). The tool resolves the contract address automatically: first checks the connected wallet's balances, then searches by symbol. Takes precedence over contractAddress when both are given.`,required:!1},{name:"contractAddress",type:"string",description:"ERC-20 token contract address (0x\u2026). Use when the exact address is already known. Ignored if tokenSymbol is provided.",required:!1},{name:"chain",type:"string",description:'Hex chain ID: "0x1" Ethereum, "0xa" Optimism, "0x38" BSC, "0x89" Polygon, "0x2105" Base, "0xa4b1" Arbitrum, "0xa86a" Avalanche, "0xe708" Linea . ONLY set this when the user EXPLICITLY names a chain; otherwise OMIT it so the connected wallet chain is used.',required:!1}];service;constructor(e){super(),this.service=new E(e)}async run(e,t){let n=R(e.chain,t),r=t?.walletAddress||void 0,o=typeof e.tokenSymbol=="string"&&e.tokenSymbol?e.tokenSymbol.trim():void 0,s=typeof e.contractAddress=="string"&&e.contractAddress?e.contractAddress.trim():void 0;if(!o&&!s)return{error:'Provide either tokenSymbol (e.g. "USDC") or contractAddress (0x\u2026).'};let a,i;if(o){let d=await this.resolveContractAddress(o,n,r);if(!d)return{error:`Cannot resolve token symbol "${o}" to a contract address on ${n?I(n):"the selected chain"}. Try passing contractAddress directly.`};s=d.address,a=d.symbol,i=d.name}let l=await this.service.getTokenHolders({address:s,chain:n});if(!l.success)return{error:l.error||"Failed to fetch token holder stats"};let u=l.data;return{token:{symbol:a??o??null,name:i??null,contractAddress:s,chain:n||"default"},totalHolders:u.totalHolders,holdersByAcquisition:u.holdersByAcquisition,holderChange:u.holderChange,holderSupply:u.holderSupply,holderDistribution:u.holderDistribution}}async resolveContractAddress(e,t,n){let r=e.toLowerCase();if(n){let s=await this.service.getWalletTokenBalances({address:n,chain:t});if(s.success&&s.data?.result?.length){let a=s.data.result.find(i=>(i.symbol??"").toLowerCase()===r);if(a)return{address:a.token_address,symbol:a.symbol,name:a.name}}}let o=await this.service.searchTokensByKey({key:e,chain:t});if(o.success&&o.data?.length){let a=o.data.find(i=>(i.symbol??"").toLowerCase()===r)??o.data[0];if(a?.token_address)return{address:a.token_address,symbol:a.symbol,name:a.name}}}};var je=class extends v{name="get-token-analytics";description=`Get detailed trading analytics for a single ERC-20 token, broken down across 5min / 1h / 6h / 24h windows. Returns: buy volume, sell volume, unique buyers, unique sellers, buy/sell transaction counts, unique wallets, price % change, current USD price, total liquidity and fully-diluted valuation (FDV). Use tokenSymbol (e.g. "PEPE") to look up by name \u2014 the tool auto-resolves the contract address. Use contractAddress when you already have the exact 0x address. Use this tool for questions like: "How is PEPE trading today?" (tokenSymbol=PEPE), "24h buy vs sell volume for USDC?" (tokenSymbol=USDC \u2192 totalBuyVolume vs totalSellVolume), "Is buying or selling pressure stronger for this token?", "How many unique wallets touched this token in the last hour?" (\u2192 uniqueWallets.1h), "What's the FDV and liquidity of X?" (\u2192 totalFullyDilutedValuation, totalLiquidity), "Price change over 6h?" (\u2192 pricePercentChange.6h). Supports: Ethereum, Optimism, BSC, Polygon, Base, Arbitrum, Avalanche, Linea.`;category="blockchain-data";parameters=[{name:"tokenSymbol",type:"string",description:`Token symbol (e.g. "PEPE", "USDC", "WETH"). The tool resolves the contract address automatically: first checks the connected wallet's balances, then searches by symbol. Takes precedence over contractAddress when both are given.`,required:!1},{name:"contractAddress",type:"string",description:"ERC-20 token contract address (0x\u2026). Use when the exact address is already known. Ignored if tokenSymbol is provided.",required:!1},{name:"chain",type:"string",description:'Hex chain ID: "0x1" Ethereum, "0xa" Optimism, "0x38" BSC, "0x89" Polygon, "0x2105" Base, "0xa4b1" Arbitrum, "0xa86a" Avalanche, "0xe708" Linea. ONLY set this when the user EXPLICITLY names a chain; otherwise OMIT it so the connected wallet chain is used.',required:!1}];service;constructor(e){super(),this.service=new E(e)}async run(e,t){let n=R(e.chain,t),r=t?.walletAddress||void 0,o=typeof e.tokenSymbol=="string"&&e.tokenSymbol?e.tokenSymbol.trim():void 0,s=typeof e.contractAddress=="string"&&e.contractAddress?e.contractAddress.trim():void 0;if(!o&&!s)return{error:'Provide either tokenSymbol (e.g. "PEPE") or contractAddress (0x\u2026).'};let a,i;if(o){let d=await this.resolveContractAddress(o,n,r);if(!d)return{error:`Cannot resolve token symbol "${o}" to a contract address on ${n?I(n):"the selected chain"}. Try passing contractAddress directly.`};s=d.address,a=d.symbol,i=d.name}let l=await this.service.getTokenAnalytics({address:s,chain:n});if(!l.success)return{error:l.error||"Failed to fetch token analytics"};let u=l.data??{};return{token:{symbol:a??o??null,name:i??null,contractAddress:s,chain:n||"default"},usdPrice:u.usdPrice??null,totalLiquidity:u.totalLiquidity??null,totalFullyDilutedValuation:u.totalFullyDilutedValuation??null,totalBuyVolume:u.totalBuyVolume??null,totalSellVolume:u.totalSellVolume??null,totalBuyers:u.totalBuyers??null,totalSellers:u.totalSellers??null,totalBuys:u.totalBuys??null,totalSells:u.totalSells??null,uniqueWallets:u.uniqueWallets??null,pricePercentChange:u.pricePercentChange??null}}async resolveContractAddress(e,t,n){let r=e.toLowerCase();if(n){let s=await this.service.getWalletTokenBalances({address:n,chain:t});if(s.success&&s.data?.result?.length){let a=s.data.result.find(i=>(i.symbol??"").toLowerCase()===r);if(a)return{address:a.token_address,symbol:a.symbol,name:a.name}}}let o=await this.service.searchTokensByKey({key:e,chain:t});if(o.success&&o.data?.length){let a=o.data.find(i=>(i.symbol??"").toLowerCase()===r)??o.data[0];if(a?.token_address)return{address:a.token_address,symbol:a.symbol,name:a.name}}}};var ze=class extends v{name="get-token-score";description=`Get a composite 0\u2013100 health/safety score for a single ERC-20 token, plus the metrics behind it. Returns: overall score, last-updated timestamp, current USD price, paired liquidity, multi-timeframe volume buckets (10m / 30m / 1h / 4h / 12h / 1d / 7d / 30d), same buckets for transaction counts, and supply info (total supply + % held by top 10 holders). Use tokenSymbol (e.g. "PEPE") to look up by name \u2014 the tool auto-resolves the contract address. Use contractAddress when you already have the exact 0x address. Use this tool for questions like: "Is PEPE a safe token?" (tokenSymbol=PEPE \u2192 score), "What's the health/score of USDC?", "How concentrated is X's supply among whales?" (\u2192 metrics.supply.top10Percent), "How much volume did this token do over the last 7 days?" (\u2192 metrics.volumeUsd.7d), "Transaction count for this token in the last hour?" (\u2192 metrics.transactions.1h). Supports: Ethereum, Optimism, BSC, Polygon, Base, Arbitrum, Avalanche, Linea.`;category="blockchain-data";parameters=[{name:"tokenSymbol",type:"string",description:`Token symbol (e.g. "PEPE", "USDC", "WETH"). The tool resolves the contract address automatically: first checks the connected wallet's balances, then searches by symbol. Takes precedence over contractAddress when both are given.`,required:!1},{name:"contractAddress",type:"string",description:"ERC-20 token contract address (0x\u2026). Use when the exact address is already known. Ignored if tokenSymbol is provided.",required:!1},{name:"chain",type:"string",description:'Hex chain ID: "0x1" Ethereum, "0xa" Optimism, "0x38" BSC, "0x89" Polygon, "0x2105" Base, "0xa4b1" Arbitrum, "0xa86a" Avalanche, "0xe708" Linea. ONLY set this when the user EXPLICITLY names a chain; otherwise OMIT it so the connected wallet chain is used.',required:!1}];service;constructor(e){super(),this.service=new E(e)}async run(e,t){let n=R(e.chain,t),r=t?.walletAddress||void 0,o=typeof e.tokenSymbol=="string"&&e.tokenSymbol?e.tokenSymbol.trim():void 0,s=typeof e.contractAddress=="string"&&e.contractAddress?e.contractAddress.trim():void 0;if(!o&&!s)return{error:'Provide either tokenSymbol (e.g. "PEPE") or contractAddress (0x\u2026).'};let a,i;if(o){let m=await this.resolveContractAddress(o,n,r);if(!m)return{error:`Cannot resolve token symbol "${o}" to a contract address on ${n?I(n):"the selected chain"}. Try passing contractAddress directly.`};s=m.address,a=m.symbol,i=m.name}let l=await this.service.getTokenScore({address:s,chain:n});if(!l.success)return{error:l.error||"Failed to fetch token score"};let u=l.data??{},d=u.metrics??{};return{token:{symbol:a??o??null,name:i??null,contractAddress:s,chain:n||"default"},score:u.score??null,updatedAt:u.updatedAt??null,metrics:{usdPrice:d.usdPrice??null,liquidityUsd:d.liquidityUsd??null,volumeUsd:d.volumeUsd??null,transactions:d.transactions??null,supply:d.supply??null}}}async resolveContractAddress(e,t,n){let r=e.toLowerCase();if(n){let s=await this.service.getWalletTokenBalances({address:n,chain:t});if(s.success&&s.data?.result?.length){let a=s.data.result.find(i=>(i.symbol??"").toLowerCase()===r);if(a)return{address:a.token_address,symbol:a.symbol,name:a.name}}}let o=await this.service.searchTokensByKey({key:e,chain:t});if(o.success&&o.data?.length){let a=o.data.find(i=>(i.symbol??"").toLowerCase()===r)??o.data[0];if(a?.token_address)return{address:a.token_address,symbol:a.symbol,name:a.name}}}};var Qe=class extends v{name="get-trending-tokens";description='List tokens trending on-chain right now, ranked by trading activity, volume, liquidity and holder growth. Returns price, market cap, liquidity, holder count, and multi-timeframe buckets (1h / 4h / 12h / 24h) of price change, volume, transaction counts, and unique buyers/sellers. Cross-chain by default \u2014 omit `chain` for a global ranking, or pass it to filter to one network. Use this tool for questions like: "What tokens are trending right now?", "Show me hot tokens", "What\'s popular on Base today?", "Which tokens have the highest 24h volume?", "Biggest movers in the last hour?". Supports: Ethereum, Optimism, BSC, Polygon, Base, Arbitrum, Avalanche, Linea, and more.';category="blockchain-data";parameters=[{name:"chain",type:"string",description:'Optional hex chain ID to scope results to a single network: "0x1" Ethereum, "0xa" Optimism, "0x38" BSC, "0x89" Polygon, "0x2105" Base, "0xa4b1" Arbitrum, "0xa86a" Avalanche, "0xe708" Linea. ONLY set this when the user names a chain. Omit to return cross-chain trending results.',required:!1},{name:"limit",type:"number",description:'Total number of trending tokens to return (chain default tokens + top gainers combined). Only set this when the user asks for a specific count (e.g. "show 20 trending tokens" \u2192 20). Omit otherwise \u2014 when omitted, returns all default tokens plus 5 top gainers.',required:!1},{name:"buy_button_label",type:"string",description:`Label template for the per-token "Buy" suggestion buttons, IN THE USER'S CURRENT LANGUAGE. One button is rendered per trending token, with "{token}" replaced by that token's symbol. Use the exact placeholder "{token}" (do not translate the braces); put the localized verb in the text. English example: "Buy {token}". Vietnamese example: "Mua {token}". Japanese example: "{token} \u8CFC\u5165". Always include {token}.`,required:!0},{name:"buy_prompt_template",type:"string",description:`A short "buy" command template IN THE USER'S CURRENT LANGUAGE, submitted as the next user turn when a token's Buy button is clicked. "{token}" is replaced by that token's symbol. Use the exact placeholder "{token}" (do not translate the braces); put the localized verb in the text. English example: "buy {token}". Vietnamese example: "mua {token}". Japanese example: "{token} \u8CFC\u5165". Always include {token}.`,required:!0}];service;constructor(e){super(),this.service=new ee({baseUrl:e?.pantographUrl})}async run(e,t){let n=wn(e.chain,t),r=e.buy_button_label||"",o=e.buy_prompt_template||"",s=typeof e.limit=="number"&&Number.isFinite(e.limit)&&e.limit>0?Math.floor(e.limit):void 0,a=await this.service.getTrendingTokens({chain:n,limit:s});if(!a.success)return{error:a.error||"Failed to fetch trending tokens"};let i=a.data??[],l=new Set,u=[];for(let d of i){let m=d.symbol?.trim();!m||l.has(m.toLowerCase())||(l.add(m.toLowerCase()),u.push({label:this.fillTemplate(r,m,"Buy {token}"),prompt:this.fillTemplate(o,m,"buy {token}")}))}return{_instructions:`Start with a one-line header like "Here are some of the top trending tokens on <chain name> right now:". Then present the tokens as a numbered list. For EACH token render exactly these lines (omit any line whose value is null):
4
4
  <rank>. <name> (<symbol>)
@@ -880,21 +880,25 @@ ${"\u2500".repeat(60)}`),console.log(`[AgentCore] query: "${e}"`));let s=await t
880
880
  Q: ${t.entry.question}
881
881
  A: ${t.entry.answer}`).join(`
882
882
 
883
- `)}async tryAnswerFromKB(e,t,n,r){if(!n||t.length===0)return this.debug&&console.log("[AgentCore] KB skip: no hits \u2192 router pipeline"),null;let o=t[0].score;if(o<this.kbAnswerThreshold)return this.debug&&console.log(`[AgentCore] KB skip: top score ${o.toFixed(3)} < threshold ${this.kbAnswerThreshold} \u2192 router pipeline`),null;this.debug&&console.log(`[AgentCore] KB answer: top score ${o.toFixed(3)} \u2265 threshold ${this.kbAnswerThreshold} \u2192 verifying intent before KB answer`);let s=t[0].entry.question,a=[{role:"system",content:`You are a STRICT intent classifier. Respond with ONLY "yes" or "no" \u2014 no other text.
883
+ `)}async tryAnswerFromKB(e,t,n,r){if(!n||t.length===0)return this.debug&&console.log("[AgentCore] KB skip: no hits \u2192 router pipeline"),null;let o=t[0].score;if(o<this.kbAnswerThreshold)return this.debug&&console.log(`[AgentCore] KB skip: top score ${o.toFixed(3)} < threshold ${this.kbAnswerThreshold} \u2192 router pipeline`),null;this.debug&&console.log(`[AgentCore] KB answer: top score ${o.toFixed(3)} \u2265 threshold ${this.kbAnswerThreshold} \u2192 verifying intent before KB answer`);let a=t.slice(0,3).map(g=>g.entry.question).filter(g=>typeof g=="string"&&g.trim().length>0).map((g,f)=>`${f+1}. "${g}"`).join(`
884
+ `),i=[{role:"system",content:`You are a STRICT intent classifier. Respond with ONLY "yes" or "no" \u2014 no other text.
885
+
886
+ You are given a User message and a numbered list of FAQ questions.
884
887
 
885
888
  Default to "no". Only answer "yes" when you are confident.
886
889
 
887
- Answer "yes" ONLY IF the user message and the FAQ question are genuinely EQUIVALENT \u2014 i.e. they ask the SAME thing and a single answer to the FAQ question would fully and directly answer the user message. Merely sharing a topic, a keyword, or a domain is NOT enough.
890
+ Answer "yes" ONLY IF the user message is genuinely EQUIVALENT to AT LEAST ONE of the FAQ questions \u2014 i.e. they ask the SAME thing and a single answer to that FAQ question would fully and directly answer the user message. Merely sharing a topic, a keyword, or a domain is NOT enough.
888
891
 
889
- Answer "no" if ANY of these hold:
892
+ Answer "no" if, for EVERY FAQ question, ANY of these hold:
890
893
  - The user is asking about a DIFFERENT aspect, sub-question, or angle of the same general topic (e.g. FAQ = "what is staking?" vs user = "how do I unstake?").
891
894
  - The user message is broader, narrower, or only partially overlaps with the FAQ question.
892
895
  - They merely share vocabulary or a domain but ask for different things.
893
- - The user is asking for real-time data, live lookups, prices, balances, transactions, or anything that requires fetching external data \u2014 even if the FAQ question looks similar.
894
- - You are uncertain.`,timestamp:0},{role:"user",content:`FAQ question: "${s}"
896
+ - The user is asking for real-time data, live lookups, prices, balances, transactions, or anything that requires fetching external data \u2014 even if a FAQ question looks similar.
897
+ - You are uncertain.`,timestamp:0},{role:"user",content:`FAQ questions:
898
+ ${a}
895
899
  User message: "${e}"
896
900
 
897
- Are these two questions EQUIVALENT \u2014 asking the same thing such that one answer fully answers both? Answer "yes" only if equivalent, otherwise "no".`,timestamp:Date.now()}],l=(await this.llm.chat(a)).text.trim().toLowerCase();if(!l.startsWith("yes"))return this.debug&&console.log(`[AgentCore] KB intent guard: verdict="${l}" \u2192 skipping KB, routing to subagent pipeline`),null;this.debug&&console.log('[AgentCore] KB intent guard: verdict="yes" \u2192 answering from KB');let u=[{role:"system",content:this.systemPrompt+`
901
+ Is the user message EQUIVALENT to AT LEAST ONE of the FAQ questions \u2014 asking the same thing such that that FAQ's answer fully answers the user message? Answer "yes" only if equivalent to one of them, otherwise "no".`,timestamp:Date.now()}],u=(await this.llm.chat(i)).text.trim().toLowerCase();if(!u.startsWith("yes"))return this.debug&&console.log(`[AgentCore] KB intent guard: verdict="${u}" \u2192 skipping KB, routing to subagent pipeline`),null;this.debug&&console.log('[AgentCore] KB intent guard: verdict="yes" \u2192 answering from KB');let d=[{role:"system",content:this.systemPrompt+`
898
902
 
899
903
  You have reference material below. Use it ONLY if it directly addresses the user's question. Rephrase and adapt the content naturally \u2014 do NOT copy it verbatim. Do NOT supplement with general knowledge or speculate beyond what the reference provides. If the reference does not fully answer the question, say so honestly.
900
904
 
@@ -903,7 +907,7 @@ REFERENCE MATERIAL:
903
907
  `+n+`
904
908
  ---`,timestamp:0},{role:"user",content:`${e}
905
909
 
906
- (You MUST reply in the same language as the question above, regardless of the reference material language.)`,timestamp:Date.now()}],m=(await this.llm.chat(u)).text,p={role:"assistant",content:m,timestamp:Date.now()};return r&&(p.messageId=r),this.history.add(p),await this.persistHistory(),{answer:m,suggestedPrompts:[]}}async answerDirectly(e,t,n,r=!0,o,s=!1){this.debug&&console.log(`[AgentCore] Router returned no assignments, answering directly via LLM\u2026 (webSearch=${s})`);let a=s?"":`
910
+ (You MUST reply in the same language as the question above, regardless of the reference material language.)`,timestamp:Date.now()}],p=(await this.llm.chat(d)).text,h={role:"assistant",content:p,timestamp:Date.now()};return r&&(h.messageId=r),this.history.add(h),await this.persistHistory(),{answer:p,suggestedPrompts:[]}}async answerDirectly(e,t,n,r=!0,o,s=!1){this.debug&&console.log(`[AgentCore] Router returned no assignments, answering directly via LLM\u2026 (webSearch=${s})`);let a=s?"":`
907
911
 
908
912
  You are answering WITHOUT live web access. Rely only on knowledge you are confident about. If the question needs real-time or recent information you do not have (current prices, today's news, latest events), say plainly that you do not have up-to-date data instead of guessing. Never fabricate numbers, dates, or facts.`,i=(n?this.systemPrompt+`
909
913
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "keyring-agent-core",
3
- "version": "0.2.12",
3
+ "version": "0.2.13",
4
4
  "description": "Core AI chat agent with multi-agent architecture, ReAct pattern, and modular tool system powered by Gemini",
5
5
  "main": "dist/index.js",
6
6
  "react-native": "dist/index.js",