keyring-agent-core 0.2.5 → 0.2.6
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.
- package/dist/index.js +29 -19
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"use strict";var Cn=Object.defineProperty;var Fo=Object.getOwnPropertyDescriptor;var Wo=Object.getOwnPropertyNames;var Vo=Object.prototype.hasOwnProperty;var Ho=(c,e)=>{for(var t in e)Cn(c,t,{get:e[t],enumerable:!0})},Go=(c,e,t,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of Wo(e))!Vo.call(c,r)&&r!==t&&Cn(c,r,{get:()=>e[r],enumerable:!(n=Fo(e,r))||n.enumerable});return c};var Ko=c=>Go(Cn({},"__esModule",{value:!0}),c);var
|
|
1
|
+
"use strict";var Cn=Object.defineProperty;var Fo=Object.getOwnPropertyDescriptor;var Wo=Object.getOwnPropertyNames;var Vo=Object.prototype.hasOwnProperty;var Ho=(c,e)=>{for(var t in e)Cn(c,t,{get:e[t],enumerable:!0})},Go=(c,e,t,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of Wo(e))!Vo.call(c,r)&&r!==t&&Cn(c,r,{get:()=>e[r],enumerable:!(n=Fo(e,r))||n.enumerable});return c};var Ko=c=>Go(Cn({},"__esModule",{value:!0}),c);var js={};Ho(js,{AI_AGENT_TOOL_NAMES:()=>kn,AgentCore:()=>An,ApproveTokenTool:()=>_t,BaseNftMessageTool:()=>ee,BaseSwapService:()=>ve,BaseTool:()=>v,BaseWalletActionTool:()=>J,BuyTokenTool:()=>Rt,ChatHistory:()=>Nt,DEFAULT_PROVIDER:()=>Vt,DEFAULT_RPC_BY_CHAIN:()=>ne,DebridgeAdapter:()=>dt,EstimatePoolYieldTool:()=>ft,GeminiProvider:()=>le,GeminiSearchAiTool:()=>nt,HEX_TO_PANTOGRAPH:()=>ro,KnowledgeBase:()=>Dt,MoralisService:()=>E,NFTContractInfoTool:()=>et,NFTMetadataTool:()=>tt,NFT_AGENT_TOOL_NAMES:()=>wn,OpenAddLiquidityFormTool:()=>ht,POOL_AGENT_TOOL_NAMES:()=>Tn,PantographService:()=>Y,PoolByAddressTool:()=>at,PoolDetailTool:()=>rt,PoolSearchTool:()=>st,PoolService:()=>_e,PreviewAddLiquidityTool:()=>gt,RelayAdapter:()=>mt,Router:()=>Lt,SWAP_PROVIDER_CONFIG:()=>mn,SendNativeTool:()=>Pt,SendNftTool:()=>Je,SendTokenTool:()=>At,Subagent:()=>V,SubgraphCoinPoolPairsTool:()=>vt,SubgraphPoolByAddressTool:()=>wt,SubgraphPoolByPositionIdTool:()=>kt,SubgraphPoolSearchTool:()=>yt,SubgraphPositionDetailTool:()=>Tt,SubgraphTrendingPoolsTool:()=>bt,Summarizer:()=>It,SwapServiceFactory:()=>Ae,SwapTokenTool:()=>Et,Synthesizer:()=>Mt,TOKEN_AGENT_TOOL_NAMES:()=>bn,TRANSFER_TOPIC:()=>xo,TRENDING_DEFAULT_BY_HEX_CHAIN:()=>so,TokenAnalyticsTool:()=>Be,TokenHoldersTool:()=>Me,TokenInfoTool:()=>Le,TokenScoreTool:()=>Oe,ToolRegistry:()=>Ee,TopGainersTool:()=>qe,TopPoolsTool:()=>ot,TransactionByHashTool:()=>Xe,TrendingTokensTool:()=>$e,UNISWAP_CHAIN_SLUG:()=>Gt,UnwrapNativeTool:()=>Ut,UpstashKnowledgeBase:()=>Se,WALLET_ACTION_AGENT_TOOL_NAMES:()=>vn,WALLET_AGENT_TOOL_NAMES:()=>yn,WalletApprovalsTool:()=>je,WalletDefiPositionsTool:()=>ze,WalletDefiProtocolPositionsTool:()=>Qe,WalletDefiSummaryTool:()=>Ye,WalletHistoryTool:()=>We,WalletNFTsTool:()=>Ze,WalletNetWorthTool:()=>on,WalletNftTransfersTool:()=>He,WalletPnlSummaryTool:()=>Ge,WalletPnlTool:()=>Ke,WalletTokenBalancesTool:()=>Fe,WalletTokenTransfersTool:()=>Ve,WrapNativeTool:()=>Ct,ZERO_ADDRESS:()=>L,buildRangePresets:()=>Fn,buildUniswapPoolUrl:()=>se,clampTick:()=>he,createAiAgent:()=>Qt,createDefaultSubagents:()=>Sn,createNftAgent:()=>zt,createPoolAgent:()=>Xt,createTokenAgent:()=>Yt,createWalletActionAgent:()=>Jt,createWalletAgent:()=>jt,ethCallAt:()=>To,ethCallByChain:()=>qn,getAffiliateFee:()=>ct,getChainMeta:()=>z,getDefaultRpcUrl:()=>Jr,getGatewayAddress:()=>hn,getNativeTokenInfo:()=>pt,getPositionManagerAddress:()=>gn,getProviderByChain:()=>dn,ingestKnowledgeBase:()=>$o,priceToTick:()=>Pe,resolveRpcUrl:()=>ut,roundTickToSpacing:()=>pe,rpcCall:()=>fe,setRpcOverrides:()=>$n,swapServiceFactory:()=>ce,tickToPrice:()=>oe,toPantographChain:()=>qt});module.exports=Ko(js);var Hn={string:"STRING",number:"NUMBER",boolean:"BOOLEAN",object:"OBJECT",array:"ARRAY"},jo="https://nft-demo.keyring.app/api/gemini-stable",Yo=new Set([408,429,500,502,503,504]),Gn=3e4,Zt=c=>new Promise(e=>setTimeout(e,c));function en(c,e,t){if(t!=null&&t>0)return Math.min(t,Gn);let n=Math.min(e*2**(c-1),Gn);return n/2+Math.random()*(n/2)}function zo(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 le=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??jo}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?.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 fetch(u,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(l)})}catch(b){if(d=b instanceof Error?b.message:String(b),m>=s)break;await Zt(en(m,a));continue}if(!p.ok){let b=await p.text().catch(()=>p.statusText);if(d=`Gemini proxy error ${p.status}: ${b}`,!Yo.has(p.status))throw new Error(d);if(m>=s)break;let T=zo(p.headers.get("retry-after"));await Zt(en(m,a,T));continue}let h;try{h=await p.json()}catch(b){if(d=`Gemini proxy returned invalid JSON: ${b instanceof Error?b.message:String(b)}`,m>=s)break;await Zt(en(m,a));continue}let f=h.candidates?.[0];if(!f)return{text:"",toolCalls:[]};let g="",y=[],k=f.content?.parts;if(!Array.isArray(k)||k.length===0){let b=f.finishReason;if(!(b==="SAFETY"||b==="RECITATION")&&m<s){d=`Gemini returned an empty turn (finishReason=${b??"none"})`,await Zt(en(m,a));continue}return{text:"",toolCalls:[]}}for(let b of k)b.text&&(g+=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:g,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:Hn[o.type]??"STRING",description:o.description};o.type==="array"&&(s.items={type:Hn[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 Ee=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 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);return{toolName:this.name,callId:n,success:!0,data:o,duration:Date.now()-r}}catch(o){return{toolName:this.name,callId:n,success:!1,error:o instanceof Error?o.message:String(o),duration:Date.now()-r}}}};var Qo="0x2105",Xo=["0x1","0xa","0x38","0x89","0x2105","0xa4b1","0xa86a","0xe708"],Jo=new Set(Xo),tn="Ethereum (0x1), Optimism (0xa), BSC (0x38), Polygon (0x89), Base (0x2105), Arbitrum (0xa4b1), Avalanche (0xa86a), Linea (0xe708)",Zo={"0x1":"0x1",1:"0x1",eth:"0x1",ether:"0x1",ethereum:"0x1",mainnet:"0x1","0xa":"0xa",10:"0xa",op:"0xa",optimism:"0xa","0x38":"0x38",56:"0x38",bsc:"0x38",bnb:"0x38",binance:"0x38","0x89":"0x89",137:"0x89",matic:"0x89",polygon:"0x89","0x2105":"0x2105",8453:"0x2105",base:"0x2105","0xa4b1":"0xa4b1",42161:"0xa4b1",arb:"0xa4b1",arbitrum:"0xa4b1","0xa86a":"0xa86a",43114:"0xa86a",avax:"0xa86a",avalanche:"0xa86a","0xe708":"0xe708",59144:"0xe708",linea:"0xe708"};function me(c){if(typeof c!="string")return null;let e=c.trim().toLowerCase();if(!e)return null;let t=Zo[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&&Jo.has(n)?n:null}function jn(c){return me(c)!==null}var Ne=class extends Error{constructor(t){super(`Chain "${t}" is not supported. Supported chains: ${tn}.`);this.requested=t;this.name="UnsupportedChainError"}requested;code="unsupported_chain"},Bt=c=>typeof c=="string"&&c.trim()!=="";function R(c,e,t=Qo){if(Bt(c)){let n=me(c);if(n)return n;throw new Ne(c.trim())}if(Bt(e?.chain)){let n=me(e.chain);if(n)return n;throw new Ne(e.chain.trim())}return t}function Yn(c,e){if(Bt(c)){let t=me(c);if(t)return t;throw new Ne(c.trim())}if(Bt(e?.chain)){let t=me(e.chain);if(t)return t;throw new Ne(e.chain.trim())}return null}function nn(c,e){return me(c)??me(e?.chain)??void 0}var Kn={"0x1":"Ethereum","0xa":"Optimism","0x38":"BSC","0x89":"Polygon","0x2105":"Base","0xa4b1":"Arbitrum","0xa86a":"Avalanche","0xe708":"Linea"};var er={bnb:"0x38",matic:"0x89",pol:"0x89",avax:"0xa86a"};function zn(c){if(typeof c!="string")return null;let e=c.trim().toLowerCase();return e?er[e]??null:null}function Ie(c,e){if(!Bt(c))return null;let t=me(c),n=me(e?.chain);return!t||!n||t===n?null:{requested:t,connected:n,requestedLabel:Kn[t]??t,connectedLabel:Kn[n]??n}}var Qn=require("js-sha3");function D(c){return typeof c=="string"&&/^0x[0-9a-fA-F]{40}$/.test(c)}function tr(c){return new Uint8Array(Qn.keccak256.arrayBuffer(c))}function Ot(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 Xn(c){return"0x"+Array.from(c).map(e=>e.toString(16).padStart(2,"0")).join("")}function Jn(c){return new TextEncoder().encode(c)}function xe(...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 or(c,e=32){let t=new Uint8Array(e);return t.set(c),t}function Zn(c){let e=((c%(1n<<256n)+(1n<<256n))%(1n<<256n)).toString(16).padStart(64,"0");return Ot(e)}function $t(c){return Zn(BigInt(c))}function eo(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 to(c){let e=eo(c.type);if(e==="tuple"||e.startsWith("tuple[")){let t=(c.components??[]).map(to).join(","),n=e.startsWith("tuple[")?e.slice(5):"";return`(${t})${n}`}return e}function rr(c){let e=(c.inputs??[]).map(to).join(",");return`${c.name??""}(${e})`}function Un(c,e){let t=ir(c.type);if(t){let[r,o]=t;return sr({...c,type:o},e,r)}if(c.type==="tuple")return ar(c,e);if(c.type==="address"){let r=String(e),o=r.startsWith("0x")?r.slice(2):r;return{dynamic:!1,encoded:nr(Ot(o.toLowerCase()))}}if(c.type==="bool"){let r=new Uint8Array(32);return r[31]=e?1:0,{dynamic:!1,encoded:r}}let n=eo(c.type);if(/^uint\d*$/.test(n))return{dynamic:!1,encoded:$t(BigInt(e))};if(/^int\d*$/.test(n)){let r=BigInt(e);return r<0n&&(r=r+(1n<<256n)),{dynamic:!1,encoded:Zn(r)}}if(/^bytes(\d+)$/.test(n)){let r=parseInt(n.slice(5),10),o=typeof e=="string"?Ot(e):e;if(o.length!==r)throw new Error(`bytes${r} expects exactly ${r} bytes, got ${o.length}`);return{dynamic:!1,encoded:or(o)}}if(n==="bytes"){let r=typeof e=="string"?Ot(e):e,o=Math.ceil(r.length/32)*32,s=new Uint8Array(o);return s.set(r),{dynamic:!0,encoded:xe($t(r.length),s)}}if(n==="string"){let r=typeof e=="string"?Jn(e):e,o=Math.ceil(r.length/32)*32,s=new Uint8Array(o);return s.set(r),{dynamic:!0,encoded:xe($t(r.length),s)}}throw new Error(`Unsupported ABI type: ${c.type}`)}function Rn(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($t(e+r)),n.push(s),r+=s.length):t.push(s);return xe(...t,...n)}function sr(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=Un(c,s);a.dynamic&&(r=!0),o.push(a)}if(n||r){let s=Rn(o);if(n){let a=$t(o.length);return{dynamic:!0,encoded:o.length>0?xe(a,s):a}}return{dynamic:!0,encoded:s}}return{dynamic:!1,encoded:xe(...o.map(s=>s.encoded))}}function ar(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=Un(s,a);i.dynamic&&(n=!0),r.push(i)}return{dynamic:n,encoded:n?Rn(r):xe(...r.map(o=>o.encoded))}}function ir(c){let e=c.match(/^(.*)\[(\d+)?\]$/);return e?[e[2]?Number(e[2]):null,e[1]]:void 0}function lr(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)=>Un(r,e[o])),n=Rn(t);return Xn(n)}function De({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=rr(n),o=tr(Jn(r)).slice(0,4),s=n.inputs??[],a=s.length===0||t.length===0?new Uint8Array(0):Ot(lr(s,t).slice(2));return Xn(xe(o,a))}var cr="https://wallet-api.pantograph.app",ur="0x0000000000000000000000000000000000000000",ro={"0x1":"ether","0xa":"optimism","0x38":"bsc","0x89":"matic","0x2105":"base","0xa4b1":"arbitrum","0xa86a":"avax","0xe708":"linea"},so={"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 qt(c){return ro[c.toLowerCase()]||c}function no(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 En(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 j(c){if(c==null)return null;let e=typeof c=="number"?c:parseFloat(String(c));return Number.isFinite(e)?e:null}function oo(c,e){let t=c.decimals,n=typeof t=="number"?t:typeof t=="string"?parseInt(t,10):null,r=j(c.price_change_percentage_24h)??j(c.priceChange24h)??j(c.usd_price_24hr_percent_change)??j(c.price_change_24h)??j(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:j(c.price)??j(c.usd_price),marketCap:j(c.market_cap)??j(c.marketCap),totalVolume:{"24h":j(c.total_volume)??j(c.volume24h)},pricePercentChange:{"24h":r}}}var Y=class{baseUrl;constructor(e){this.baseUrl=(e?.baseUrl??cr).replace(/\/+$/,"")}async enrichTokenPrices(e,t){if(e.length!==0)try{let n=qt(t),r=e.map(u=>u.token_address).join(","),o=`${this.baseUrl}/keyrings/tokens/${n}/v2?addresses=${encodeURIComponent(r)}`,s=await fetch(o);if(!s.ok)return;let a=await s.json(),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=qt(t),r=`${this.baseUrl}/keyrings/tokens/${n}/v2?addresses=${encodeURIComponent(e)}`,o=await fetch(r);if(!o.ok)return null;let s=await o.json(),a=Array.isArray(s)?s:Array.isArray(s.data)?s.data:[],i=e.toLowerCase()===ur,l=a.find(u=>u.address?.toLowerCase()===e.toLowerCase()||i&&u.address===""||u.token_address?.toLowerCase()===e.toLowerCase()||i&&u.token_address==="");return l?En(l):null}catch{return null}}async getTokensMetadata(e,t){let n={};if(e.length===0)return n;try{let r=qt(t),o=`${this.baseUrl}/keyrings/tokens/${r}/v2?addresses=${encodeURIComponent(e.join(","))}`,s=await fetch(o);if(!s.ok)return n;let a=await s.json(),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]=En(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||"0x2105",16);if(isNaN(o))return{success:!1,error:`Invalid hex chain: ${n}`};try{let s=no({chainId:o,key:t}),a=`${this.baseUrl}/token-list?${s}`,i=await fetch(a);if(!i.ok)throw new Error(`HTTP ${i.status}`);let l=await i.json(),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(En)}}catch(s){return{success:!1,error:s instanceof Error?s.message:"Unknown error"}}}async getTrendingTokens(e){let t=e?.chain||"0x2105",n=5,r=e?.limit&&e.limit>0?Math.floor(e.limit):void 0;try{let o=so[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(f=>i.value[f.toLowerCase()]).filter(f=>f!=null).map(f=>oo(f,t)):[],d=new Set,m=[];for(let f of u){let g=f.tokenAddress?.toLowerCase();!g||d.has(g)||(d.add(g),m.push(f))}let p=[];for(let f of l){let g=f.tokenAddress?.toLowerCase();!g||d.has(g)||(d.add(g),p.push(f))}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=j(s.market_cap)??j(s.marketCap)),o.totalVolume?.["24h"]==null&&(o.totalVolume={"24h":j(s.total_volume)??j(s.volume24h)}))}}async fetchTopGainers(e,t,n="24h"){let r=qt(e),o=no({duration:n,limit:t}),s=`${this.baseUrl}/token-list/top-gainers/${r}${o?`?${o}`:""}`,a=await fetch(s);if(!a.ok)throw new Error(`HTTP ${a.status}`);let i=await a.json();return(Array.isArray(i)?i:Array.isArray(i.data)?i.data:[]).map(u=>oo(u,e))}async getTopGainers(e){let t=e?.chain||"0x2105",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 dr="https://nft.keyring.app",mr="https://nft-demo.keyring.app",pr=.01,U=5,B=1e3,hr="0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",gr="0x0000000000000000000000000000000000000000";function F(c){return c||"0x2105"}function O(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 $(c){return new Promise(e=>setTimeout(e,c))}function fr(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 Nn(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??dr).replace(/\/+$/,""),this.v1BaseUrl=(e?.v1BaseUrl??mr).replace(/\/+$/,""),this.pantograph=new Y({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=F(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=O(u),f=`${this.baseUrl}/api/moralis/proxy/wallets/${encodeURIComponent(t)}/tokens?${h}`,g=await fetch(f);if(!g.ok)throw new Error(`HTTP ${g.status}`);let y=await g.json(),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>=pr)),{success:!0,data:p}}catch(u){i=u instanceof Error?u.message:"Unknown error",l<U&&await $(B)}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=F(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([fetch(`${this.baseUrl}/api/moralis/proxy/erc20/metadata?${O({chain:r,addresses:[t]})}`),fetch(`${this.baseUrl}/api/moralis/proxy/erc20/${encodeURIComponent(t)}/price?${O({chain:r,include:"percent_change"})}`)]),u=null;if(i.status==="fulfilled"&&i.value.ok){let m=await i.value.json();u=(Array.isArray(m)?m:Array.isArray(m?.data)?m.data:[])[0]??null}if(!u){o="Token metadata not found",a<U&&await $(B);continue}let d=null;if(l.status==="fulfilled"&&l.value.ok){let m=await l.value.json();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 $(B)}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=F(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=O(p),f=`${this.baseUrl}/api/moralis/proxy/${encodeURIComponent(t)}/nft?${h}`,g=await fetch(f);if(!g.ok)throw new Error(`HTTP ${g.status}`);let y=await g.json();return{success:!0,data:y?.data??y}}catch(p){d=p instanceof Error?p.message:"Unknown error",m<U&&await $(B)}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(!D(t))return{success:!1,error:"NFT metadata lookup by token ID is only supported on EVM chains"};let l=F(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=O(m),h=`${this.baseUrl}/api/moralis/proxy/nft/${encodeURIComponent(t)}/${encodeURIComponent(n)}?${p}`,f=await fetch(h);if(!f.ok)throw new Error(`HTTP ${f.status}`);let g=await f.json();return{success:!0,data:g?.data??g}}catch(m){u=m instanceof Error?m.message:"Unknown error",d<U&&await $(B)}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=F(n),o="Unknown error";for(let s=1;s<=U;s++)try{let a=O({chain:r}),i=`${this.baseUrl}/api/moralis/proxy/nft/${encodeURIComponent(t)}/metadata?${a}`,l=await fetch(i);if(!l.ok)throw new Error(`HTTP ${l.status}`);let u=await l.json();return{success:!0,data:u?.data??u}}catch(a){o=a instanceof Error?a.message:"Unknown error",s<U&&await $(B)}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(!D(t))return{success:!1,error:"Wallet history is only supported for EVM addresses"};let p=F(n),h=Math.max(1,Math.min(100,Math.floor(r))),f="Unknown error";for(let g=1;g<=U;g++)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=O(y),w=`${this.baseUrl}/api/moralis/proxy/wallets/${encodeURIComponent(t)}/history?${k}`,b=await fetch(w);if(!b.ok)throw new Error(`HTTP ${b.status}`);let T=await b.json();return{success:!0,data:T?.data??T}}catch(y){f=y instanceof Error?y.message:"Unknown error",g<U&&await $(B)}return{success:!1,error:f}}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(!D(t))return{success:!1,error:"ERC-20 transfers are only supported for EVM addresses"};let m=F(n),p=Math.max(1,Math.min(100,Math.floor(o))),h="Unknown error";for(let f=1;f<=U;f++)try{let g={chain:m,limit:p,order:d};s&&(g.from_date=s),a&&(g.to_date=a),i!=null&&(g.from_block=i),l!=null&&(g.to_block=l),u&&(g.cursor=u),r?.length&&(g.contract_addresses=r);let y=O(g),k=`${this.baseUrl}/api/moralis/proxy/${encodeURIComponent(t)}/erc20/transfers?${y}`,w=await fetch(k);if(!w.ok)throw new Error(`HTTP ${w.status}`);let b=await w.json();return{success:!0,data:b?.data??b}}catch(g){h=g instanceof Error?g.message:"Unknown error",f<U&&await $(B)}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(!D(t))return{success:!1,error:"NFT transfers are only supported for EVM addresses"};let h=F(n),f=Math.max(1,Math.min(100,Math.floor(o))),g="Unknown error";for(let y=1;y<=U;y++)try{let k={chain:h,limit:f,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=O(k),b=`${this.baseUrl}/api/moralis/proxy/${encodeURIComponent(t)}/nft/transfers?${w}`,T=await fetch(b);if(!T.ok)throw new Error(`HTTP ${T.status}`);let P=await T.json();return{success:!0,data:P?.data??P}}catch(k){g=k instanceof Error?k.message:"Unknown error",y<U&&await $(B)}return{success:!1,error:g}}async getTokenHolders(e){let{address:t,chain:n}=e;if(!t)return{success:!1,error:"Contract address is required"};if(!D(t))return{success:!1,error:"Token holders is only supported for EVM contract addresses"};let r=F(n),o="Unknown error";for(let s=1;s<=U;s++)try{let a=O({chain:r}),i=`${this.baseUrl}/api/moralis/proxy/erc20/${encodeURIComponent(t)}/holders?${a}`,l=await fetch(i);if(!l.ok)throw new Error(`HTTP ${l.status}`);let u=await l.json();return{success:!0,data:u?.data??u}}catch(a){o=a instanceof Error?a.message:"Unknown error",s<U&&await $(B)}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(!D(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=>F(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=O(u),m=`${this.baseUrl}/api/moralis/proxy/wallets/${encodeURIComponent(t)}/net-worth${d?`?${d}`:""}`,p=await fetch(m);if(!p.ok)throw new Error(`HTTP ${p.status}`);let h=await p.json();return{success:!0,data:h?.data??h}}catch(u){i=u instanceof Error?u.message:"Unknown error",l<U&&await $(B)}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(!D(t))return{success:!1,error:"Wallet PnL summary is only supported for EVM addresses"};let o=F(n),s="Unknown error";for(let a=1;a<=U;a++)try{let i={chain:o};r&&(i.days=r);let l=O(i),u=`${this.baseUrl}/api/moralis/proxy/wallets/${encodeURIComponent(t)}/profitability/summary?${l}`,d=await fetch(u);if(!d.ok)throw new Error(`HTTP ${d.status}`);let m=await d.json();return{success:!0,data:m?.data??m}}catch(i){s=i instanceof Error?i.message:"Unknown error",a<U&&await $(B)}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(!D(t))return{success:!1,error:"Wallet PnL is only supported for EVM addresses"};let s=F(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=O(l),d=`${this.baseUrl}/api/moralis/proxy/wallets/${encodeURIComponent(t)}/profitability?${u}`,m=await fetch(d);if(!m.ok)throw new Error(`HTTP ${m.status}`);let p=await m.json();return{success:!0,data:p?.data??p}}catch(l){a=l instanceof Error?l.message:"Unknown error",i<U&&await $(B)}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=F(n),o="Unknown error";for(let s=1;s<=U;s++)try{let a=O({chain:r}),i=`${this.baseUrl}/api/moralis/proxy/transaction/${encodeURIComponent(t)}/verbose?${a}`,l=await fetch(i);if(!l.ok)throw new Error(`HTTP ${l.status}`);let u=await l.json();return{success:!0,data:u?.data??u}}catch(a){o=a instanceof Error?a.message:"Unknown error",s<U&&await $(B)}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(!D(t))return{success:!1,error:"Wallet approvals are only supported for EVM addresses"};let s=F(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=O(l),d=`${this.baseUrl}/api/moralis/proxy/wallets/${encodeURIComponent(t)}/approvals?${u}`,m=await fetch(d);if(!m.ok)throw new Error(`HTTP ${m.status}`);let p=await m.json();return{success:!0,data:p?.data??p}}catch(l){a=l instanceof Error?l.message:"Unknown error",i<U&&await $(B)}return{success:!1,error:a}}async getWalletDefiSummary(e){let{address:t,chain:n}=e;if(!t)return{success:!1,error:"Address is required"};if(!D(t))return{success:!1,error:"Wallet DeFi summary is only supported for EVM addresses"};let r=F(n),o="Unknown error";for(let s=1;s<=U;s++)try{let a=O({chains:r}),i=`${this.v1BaseUrl}/api/moralis-v1/proxy/wallets/${encodeURIComponent(t)}/defi/summary?${a}`,l=await fetch(i);if(!l.ok)throw new Error(`HTTP ${l.status}`);let u=await l.json();return{success:!0,data:u?.data??u}}catch(a){o=a instanceof Error?a.message:"Unknown error",s<U&&await $(B)}return{success:!1,error:o}}async getWalletDefiPositions(e){let{address:t,chain:n}=e;if(!t)return{success:!1,error:"Address is required"};if(!D(t))return{success:!1,error:"Wallet DeFi positions are only supported for EVM addresses"};let r=F(n),o="Unknown error";for(let s=1;s<=U;s++)try{let a=O({chains:r}),i=`${this.v1BaseUrl}/api/moralis-v1/proxy/wallets/${encodeURIComponent(t)}/defi/positions?${a}`,l=await fetch(i);if(!l.ok)throw new Error(`HTTP ${l.status}`);let u=await l.json(),d=u?.data??u;return{success:!0,data:fr(d)}}catch(a){o=a instanceof Error?a.message:"Unknown error",s<U&&await $(B)}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(!D(t))return{success:!1,error:"Wallet DeFi protocol positions are only supported for EVM addresses"};let o=F(r),s="Unknown error";for(let a=1;a<=U;a++)try{let i=O({chains:o}),l=`${this.v1BaseUrl}/api/moralis-v1/proxy/wallets/${encodeURIComponent(t)}/defi/${encodeURIComponent(n)}/positions?${i}`,u=await fetch(l);if(!u.ok)throw new Error(`HTTP ${u.status}`);let p=(await u.json()).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(f=>({label:f.label,address:f.address,balance_usd:f.balanceUsd??0,total_unclaimed_usd_value:f.unclaimedUsd??0,position_details:f.details,tokens:f.tokens.map(g=>({token_type:g.tokenType,address:g.address,contract_address:g.address,name:g.name??"",symbol:g.symbol??"",decimals:g.decimals??18,logo:g.logo,balance:g.balance??"0",balance_formatted:g.balanceFormatted??"0",usd_price:g.usdPrice,usd_value:g.usdValue}))}))}}}catch(i){s=i instanceof Error?i.message:"Unknown error",a<U&&await $(B)}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(!D(t))return{success:!1,error:"Token analytics is only supported for EVM contract addresses"};let r=F(n),o="Unknown error";for(let s=1;s<=U;s++)try{let a=O({chain:r}),i=`${this.baseUrl}/api/moralis/proxy/tokens/${encodeURIComponent(t)}/analytics?${a}`,l=await fetch(i);if(!l.ok)throw new Error(`HTTP ${l.status}`);let u=await l.json();return{success:!0,data:u?.data??u}}catch(a){o=a instanceof Error?a.message:"Unknown error",s<U&&await $(B)}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(!D(t))return{success:!1,error:"Token score is only supported for EVM contract addresses"};let r=F(n),o="Unknown error";for(let s=1;s<=U;s++)try{let a=O({chain:r}),i=`${this.baseUrl}/api/moralis/proxy/tokens/${encodeURIComponent(t)}/score?${a}`,l=await fetch(i);if(!l.ok)throw new Error(`HTTP ${l.status}`);let u=await l.json();return{success:!0,data:u?.data??u}}catch(a){o=a instanceof Error?a.message:"Unknown error",s<U&&await $(B)}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=F(t)),n!=null&&(s.limit=n);let a=O(s),i=`${this.baseUrl}/api/moralis/proxy/tokens/trending${a?`?${a}`:""}`,l=await fetch(i);if(!l.ok)throw new Error(`HTTP ${l.status}`);let u=await l.json(),d=u?.data??u;return{success:!0,data:Nn(d)}}catch(s){r=s instanceof Error?s.message:"Unknown error",o<U&&await $(B)}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=F(t)),n&&(i.time_frame=n),r!=null&&(i.min_market_cap=r),o!=null&&(i.security_score=o);let l=O(i),u=`${this.baseUrl}/api/moralis/proxy/discovery/tokens/top-gainers${l?`?${l}`:""}`,d=await fetch(u);if(!d.ok)throw new Error(`HTTP ${d.status}`);let m=await d.json(),p=m?.data??m;return{success:!0,data:Nn(p)}}catch(i){s=i instanceof Error?i.message:"Unknown error",a<U&&await $(B)}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=F(t)),n&&(i.time_frame=n),r!=null&&(i.min_market_cap=r),o!=null&&(i.security_score=o);let l=O(i),u=`${this.baseUrl}/api/moralis/proxy/discovery/tokens/top-losers${l?`?${l}`:""}`,d=await fetch(u);if(!d.ok)throw new Error(`HTTP ${d.status}`);let m=await d.json(),p=m?.data??m;return{success:!0,data:Nn(p)}}catch(i){s=i instanceof Error?i.message:"Unknown error",a<U&&await $(B)}return{success:!1,error:s}}async enrichTokenPrices(e,t){return this.pantograph.enrichTokenPrices(e,t)}};var Le=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} \u3092\u8CB7\u3046". 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} \u3092\u8CB7\u3046". Always include {token}.`,required:!0}];service;llm;constructor(e){super(),this.service=new E(e),this.llm=new le({})}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)),f=null,g=h[0];if(g?.token_address){let b=await this.service.getTokenMetadata({address:g.token_address,chain:t});b.success&&b.data&&(f=b.data)}!f&&g&&(f={...g});let y=f?.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 % (use \u25B2 for positive, \u25BC 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:f,...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 Me=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 chain ${n??"default"}. 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 Be=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 chain ${n??"default"}. 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 Oe=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 chain ${n??"default"}. 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 $e=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} \u3092\u8CB7\u3046". 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} \u3092\u8CB7\u3046". Always include {token}.`,required:!0}];service;constructor(e){super(),this.service=new Y({baseUrl:e?.pantographUrl})}async run(e,t){let n=nn(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>)
|
|
@@ -242,7 +242,15 @@ if no specific structured tool obviously fits \u2014 the agent will fall back to
|
|
|
242
242
|
- ONLY use subagent names from the provided list. NEVER invent names.
|
|
243
243
|
- Keep each "query" focused and concise \u2014 do not copy the full user message if only part belongs to that subagent.
|
|
244
244
|
- Do NOT include tool names, chain ids, or implementation details in the query field.
|
|
245
|
-
- Match the user's language in all fields
|
|
245
|
+
- Match the user's language in all fields.
|
|
246
|
+
|
|
247
|
+
## LANGUAGE
|
|
248
|
+
|
|
249
|
+
The query may arrive in ANY language (English, Vietnamese, Japanese, Korean, Chinese, Thai, Spanish, Arabic, \u2026).
|
|
250
|
+
The subagent descriptions are written in English, but they apply equally to the SAME intent expressed in
|
|
251
|
+
any language. Route by MEANING, never by matching English keywords or script \u2014 a wallet / token / NFT / pool /
|
|
252
|
+
swap / buy request is the same intent however it is phrased or whichever language it is written in. Token
|
|
253
|
+
symbols, contract addresses, and numbers are language-neutral.`,Lt=class{llm;historyMessages;debug;constructor(e,t){this.llm=e,this.historyMessages=t?.historyMessages??10,this.debug=t?.debug??!1}async route(e,t,n,r,o=[]){let s=n.map(b=>`### ${b.name} (domain: ${b.domain})
|
|
246
254
|
${b.description}`).join(`
|
|
247
255
|
|
|
248
256
|
`),a=[];r?.walletAddress&&a.push(`Connected wallet: ${r.walletAddress}`),r?.chain&&a.push(`Current chain: ${r.chain}`);let i=a.length>0?`
|
|
@@ -257,12 +265,14 @@ ${s}${i}${l}
|
|
|
257
265
|
|
|
258
266
|
User query: ${e}`,timestamp:Date.now()}],p=Date.now(),h=null,f="",g=null;for(let b=1;b<=2;b++){let T=await this.llm.chat(m);f=T.text;try{h=this.parseJSON(T.text);break}catch(P){g=P,b<2&&(this.debug&&console.log(`[Router] JSON parse failed (attempt ${b}) \u2014 re-prompting for JSON`),m.push({role:"assistant",content:T.text||"",timestamp:Date.now()}),m.push({role:"user",content:'That response was not valid JSON. Do NOT answer the user and do NOT ask any questions. Reply with ONLY the routing JSON object ({"analysis": "...", "assignments": [...]}) and nothing else.',timestamp:Date.now()}))}}let y=Date.now()-p;if(!h)return this.debug&&(console.log(`[Router] JSON parse failed after retry: ${g}`),console.log(`[Router] raw response: ${f.slice(0,300)}`)),{analysis:"Router could not parse response.",assignments:[]};let k=new Set(n.map(b=>b.name)),w=Array.isArray(h.assignments)?h.assignments.filter(b=>!!b&&typeof b.subagent=="string").filter(b=>k.has(b.subagent)).map(b=>({subagent:b.subagent,query:typeof b.query=="string"&&b.query?b.query:e,reasoning:typeof b.reasoning=="string"?b.reasoning:""})):[];if(this.debug){let b=w.length>0?w.map(T=>T.subagent).join(", "):"(none \u2014 direct answer)";if(console.log(`
|
|
259
267
|
[Router] (${y}ms)`),console.log(` analysis : ${h.analysis??""}`),console.log(` dispatch : ${b}`),w.length>0)for(let T of w)console.log(` \u2192 ${T.subagent}: "${T.query}"`),console.log(` reason: ${T.reasoning}`)}return{analysis:typeof h.analysis=="string"?h.analysis:"",assignments:w}}async dispatch(e,t,n,r){if(e.assignments.length===0)return[];this.debug&&console.log(`
|
|
260
|
-
[Router] dispatching ${e.assignments.length} subagent(s) in parallel\u2026`);let o=Date.now(),s=e.assignments.map(async i=>{let l=t.get(i.subagent);if(!l)return this.debug&&console.log(`[Router] WARN: subagent "${i.subagent}" not registered`),{subagentName:i.subagent,steps:[],finalAnswer:`Subagent "${i.subagent}" is not registered.`,toolResults:[],toolMessages:[]};let u=Date.now(),d=await l.run({query:i.query,reasoning:i.reasoning},n,r);if(this.debug){let m=d.toolResults.map(p=>p.toolName).join(", ")||"\u2014";console.log(`[Router] ${i.subagent} done in ${Date.now()-u}ms | tools: ${m}`)}return d}),a=await Promise.all(s);return this.debug&&console.log(`[Router] all subagents done in ${Date.now()-o}ms total`),a}parseJSON(e){let t=e.trim();t.startsWith("```")&&(t=t.replace(/^```(?:json)?\s*/,"").replace(/```\s*$/,"").trim());try{return JSON.parse(t)}catch(n){let r=t.match(/\{[\s\S]*\}/);if(r)return JSON.parse(r[0]);throw n}}};var
|
|
268
|
+
[Router] dispatching ${e.assignments.length} subagent(s) in parallel\u2026`);let o=Date.now(),s=e.assignments.map(async i=>{let l=t.get(i.subagent);if(!l)return this.debug&&console.log(`[Router] WARN: subagent "${i.subagent}" not registered`),{subagentName:i.subagent,steps:[],finalAnswer:`Subagent "${i.subagent}" is not registered.`,toolResults:[],toolMessages:[]};let u=Date.now(),d=await l.run({query:i.query,reasoning:i.reasoning},n,r);if(this.debug){let m=d.toolResults.map(p=>p.toolName).join(", ")||"\u2014";console.log(`[Router] ${i.subagent} done in ${Date.now()-u}ms | tools: ${m}`)}return d}),a=await Promise.all(s);return this.debug&&console.log(`[Router] all subagents done in ${Date.now()-o}ms total`),a}parseJSON(e){let t=e.trim();t.startsWith("```")&&(t=t.replace(/^```(?:json)?\s*/,"").replace(/```\s*$/,"").trim());try{return JSON.parse(t)}catch(n){let r=t.match(/\{[\s\S]*\}/);if(r)return JSON.parse(r[0]);throw n}}};var Is=`
|
|
269
|
+
|
|
270
|
+
LANGUAGE-AGNOSTIC TOOL SELECTION: The user query may arrive in ANY language (English, Vietnamese, Japanese, Korean, Chinese, Thai, Spanish, Arabic, \u2026). Every intent\u2192tool mapping, verb, and example phrase in this prompt is ILLUSTRATIVE ONLY \u2014 written mostly in English for brevity, NOT a list of accepted words. Always pick the tool by the MEANING of the request, treating translated, transliterated, or mixed-language equivalents exactly like the English example (buy / send / swap / approve / show / search / trending / balance, etc. are the same intent in every language). Token symbols, contract addresses, and numbers are language-neutral \u2014 extract them verbatim. Never refuse, misroute, or fall back to plain text merely because the wording is not in English.`,V=class{name;description;domain;toolNames;systemPrompt;cortexFallbackTool;mustCallTool;llm;registry;maxToolCalls;historyMessages;debug;constructor(e){if(this.name=e.name,this.description=e.description,this.domain=e.domain,this.toolNames=new Set(e.toolNames),this.systemPrompt=e.systemPrompt+Is,this.cortexFallbackTool=e.cortexFallbackTool,this.mustCallTool=e.mustCallTool??!1,this.cortexFallbackTool&&!this.toolNames.has(this.cortexFallbackTool))throw new Error(`Subagent "${e.name}": cortexFallbackTool "${this.cortexFallbackTool}" must be in toolNames.`);this.llm=e.llm,this.registry=e.registry,this.maxToolCalls=e.options?.maxToolCalls??5,this.historyMessages=e.options?.historyMessages??10,this.debug=e.options?.debug??!1}getCard(){return{name:this.name,description:this.description,domain:this.domain}}getTools(){return this.registry.getDefinitions().filter(e=>this.toolNames.has(e.name))}async run(e,t,n){let r=this.getTools();if(this.debug&&(console.log(`
|
|
261
271
|
[${this.name}] query: "${e.query}"`),console.log(` [${this.name}] tools available: ${r.map(T=>T.name).join(", ")}`)),r.length===0)return{subagentName:this.name,steps:[],finalAnswer:`Subagent "${this.name}" has no registered tools available.`,toolResults:[],toolMessages:[]};let o=[],s=[],a=[],i=new Set,l=0,u=!1,d=[];n?.walletAddress&&d.push(`Connected wallet: ${n.walletAddress}`),n?.chain&&d.push(`Current chain: ${n.chain}`);let m=d.length>0?`User context:
|
|
262
272
|
${d.join(`
|
|
263
273
|
`)}`:"",p=e.language?`The user's current language is "${e.language}" (BCP-47). Write every user-facing string you produce \u2014 replies AND any localized tool arguments \u2014 in this language.`:"",h=[m,p,`Routed query: ${e.query}`,e.reasoning?`Router reasoning: ${e.reasoning}`:"","Use only the tools you own. If you already have enough information, respond directly in the user's language."].filter(Boolean).join(`
|
|
264
274
|
|
|
265
|
-
`),f=Ro(t,this.toolNames),g=ye(f,this.historyMessages),y=[{role:"system",content:this.systemPrompt,timestamp:0},...g,{role:"user",content:h,timestamp:Date.now()}],k=6e3,w=this.maxToolCalls+2;for(let T=0;T<w;T++){let P=l<this.maxToolCalls,x=P?r:void 0,A=await this.llm.chat(y,x);if(A.toolCalls.length===0){if(l===0&&this.cortexFallbackTool&&P){this.debug&&console.log(` [${this.name}] no tool called \u2014 forcing fallback to ${this.cortexFallbackTool}`),y.push({role:"user",content:`You answered without calling any tool. This is not allowed for on-chain queries. Call "${this.cortexFallbackTool}" now with a clear, well-formed English prompt that captures the user's intent. Resolve ambiguity with sensible defaults (chain: Ethereum unless specified; result count: top 10; time window: last 24h; sort key: 24h volume for "top/best/trending" queries). Do NOT answer in text \u2014 call the tool.`,timestamp:Date.now()});continue}if(l===0&&this.mustCallTool&&P&&!u){u=!0,this.debug&&console.log(` [${this.name}] no tool called \u2014 forcing the LLM to pick a tool`),y.push({role:"user",content:"You answered in text without calling any tool. That is not allowed here: this request needs you to open a form. Call exactly ONE of your tools that matches the user's intent now. Pass only the fields the user actually gave; leave everything else blank \u2014 the tool/form collects the rest (including a token/amount picker when needed). Do NOT ask the user for missing fields. Do NOT answer in text \u2014 call the tool.",timestamp:Date.now()});continue}return this.debug&&console.log(` [${this.name}] done \u2014 ${l} tool call(s), answer: "${A.text.slice(0,80)}\u2026"`),{subagentName:this.name,steps:o,finalAnswer:A.text,toolResults:s,toolMessages:a}}let I={role:"assistant",content:A.text||"",toolCalls:A.toolCalls,timestamp:Date.now()};y.push(I);let S=[];for(let _ of A.toolCalls){if(!this.toolNames.has(_.toolName)){let G=`Tool "${_.toolName}" is not owned by ${this.name}`;this.debug&&console.log(` [${this.name}] WARN: ${G}`),o.push({phase:"observe",content:G,timestamp:Date.now()}),y.push({role:"tool",content:G,toolName:_.toolName,toolCallId:_.callId,timestamp:Date.now()});continue}let C=`${_.toolName}::${JSON.stringify(_.args)}`;if(i.has(C)){let G=`Skipped duplicate call: ${_.toolName}`;this.debug&&console.log(` [${this.name}] SKIP duplicate: ${_.toolName}`),o.push({phase:"observe",content:G,timestamp:Date.now()}),y.push({role:"tool",content:G,toolName:_.toolName,toolCallId:_.callId,timestamp:Date.now()});continue}i.add(C),l++,this.debug&&console.log(` [${this.name}] call #${l}: ${_.toolName} ${JSON.stringify(_.args)}`),o.push({phase:"think",content:A.text||`Calling ${_.toolName}`,timestamp:Date.now()}),o.push({phase:"act",content:`Calling tool: ${_.toolName}`,toolCall:{toolName:_.toolName,args:_.args},timestamp:Date.now()});let q=Date.now(),K=await this.registry.execute(_.toolName,_.args,n);s.push(K);let H=K.success?JSON.stringify(K.data):`Error: ${K.error}`;if(this.debug){let G=K.success?"OK":"ERR",be=K.success?`${H.slice(0,120)}${H.length>120?"\u2026":""}`:K.error;console.log(` [${this.name}] ${G} (${Date.now()-q}ms): ${be}`)}o.push({phase:"observe",content:H,toolResult:K,timestamp:Date.now()});let ae={role:"tool",content:H,toolName:_.toolName,toolCallId:_.callId,timestamp:Date.now()};y.push(ae),S.push({...ae,content:H.length>k?H.slice(0,k)+"\u2026[truncated]":H})}S.length>0&&(a.push(I),a.push(...S))}this.debug&&console.log(` [${this.name}] budget exhausted after ${l} calls, forcing final answer`),y.push({role:"user",content:"Tool budget reached. Give your best answer now based on the data gathered.",timestamp:Date.now()});let b=await this.llm.chat(y);return{subagentName:this.name,steps:o,finalAnswer:b.text,toolResults:s,toolMessages:a}}};var
|
|
275
|
+
`),f=Ro(t,this.toolNames),g=ye(f,this.historyMessages),y=[{role:"system",content:this.systemPrompt,timestamp:0},...g,{role:"user",content:h,timestamp:Date.now()}],k=6e3,w=this.maxToolCalls+2;for(let T=0;T<w;T++){let P=l<this.maxToolCalls,x=P?r:void 0,A=await this.llm.chat(y,x);if(A.toolCalls.length===0){if(l===0&&this.cortexFallbackTool&&P){this.debug&&console.log(` [${this.name}] no tool called \u2014 forcing fallback to ${this.cortexFallbackTool}`),y.push({role:"user",content:`You answered without calling any tool. This is not allowed for on-chain queries. Call "${this.cortexFallbackTool}" now with a clear, well-formed English prompt that captures the user's intent. Resolve ambiguity with sensible defaults (chain: Ethereum unless specified; result count: top 10; time window: last 24h; sort key: 24h volume for "top/best/trending" queries). Do NOT answer in text \u2014 call the tool.`,timestamp:Date.now()});continue}if(l===0&&this.mustCallTool&&P&&!u){u=!0,this.debug&&console.log(` [${this.name}] no tool called \u2014 forcing the LLM to pick a tool`),y.push({role:"user",content:"You answered in text without calling any tool. That is not allowed here: this request needs you to open a form. Call exactly ONE of your tools that matches the user's intent now. Pass only the fields the user actually gave; leave everything else blank \u2014 the tool/form collects the rest (including a token/amount picker when needed). Do NOT ask the user for missing fields. Do NOT answer in text \u2014 call the tool.",timestamp:Date.now()});continue}return this.debug&&console.log(` [${this.name}] done \u2014 ${l} tool call(s), answer: "${A.text.slice(0,80)}\u2026"`),{subagentName:this.name,steps:o,finalAnswer:A.text,toolResults:s,toolMessages:a}}let I={role:"assistant",content:A.text||"",toolCalls:A.toolCalls,timestamp:Date.now()};y.push(I);let S=[];for(let _ of A.toolCalls){if(!this.toolNames.has(_.toolName)){let G=`Tool "${_.toolName}" is not owned by ${this.name}`;this.debug&&console.log(` [${this.name}] WARN: ${G}`),o.push({phase:"observe",content:G,timestamp:Date.now()}),y.push({role:"tool",content:G,toolName:_.toolName,toolCallId:_.callId,timestamp:Date.now()});continue}let C=`${_.toolName}::${JSON.stringify(_.args)}`;if(i.has(C)){let G=`Skipped duplicate call: ${_.toolName}`;this.debug&&console.log(` [${this.name}] SKIP duplicate: ${_.toolName}`),o.push({phase:"observe",content:G,timestamp:Date.now()}),y.push({role:"tool",content:G,toolName:_.toolName,toolCallId:_.callId,timestamp:Date.now()});continue}i.add(C),l++,this.debug&&console.log(` [${this.name}] call #${l}: ${_.toolName} ${JSON.stringify(_.args)}`),o.push({phase:"think",content:A.text||`Calling ${_.toolName}`,timestamp:Date.now()}),o.push({phase:"act",content:`Calling tool: ${_.toolName}`,toolCall:{toolName:_.toolName,args:_.args},timestamp:Date.now()});let q=Date.now(),K=await this.registry.execute(_.toolName,_.args,n);s.push(K);let H=K.success?JSON.stringify(K.data):`Error: ${K.error}`;if(this.debug){let G=K.success?"OK":"ERR",be=K.success?`${H.slice(0,120)}${H.length>120?"\u2026":""}`:K.error;console.log(` [${this.name}] ${G} (${Date.now()-q}ms): ${be}`)}o.push({phase:"observe",content:H,toolResult:K,timestamp:Date.now()});let ae={role:"tool",content:H,toolName:_.toolName,toolCallId:_.callId,timestamp:Date.now()};y.push(ae),S.push({...ae,content:H.length>k?H.slice(0,k)+"\u2026[truncated]":H})}S.length>0&&(a.push(I),a.push(...S))}this.debug&&console.log(` [${this.name}] budget exhausted after ${l} calls, forcing final answer`),y.push({role:"user",content:"Tool budget reached. Give your best answer now based on the data gathered.",timestamp:Date.now()});let b=await this.llm.chat(y);return{subagentName:this.name,steps:o,finalAnswer:b.text,toolResults:s,toolMessages:a}}};var Ds=`You are the Wallet domain expert. You answer questions about a specific wallet's on-chain state.
|
|
266
276
|
|
|
267
277
|
SPECIALISED TOOLS (prefer these when intent matches):
|
|
268
278
|
- get-wallet-token-balances: token + native coin balances
|
|
@@ -313,7 +323,7 @@ PAGINATION:
|
|
|
313
323
|
pass the cursor from the LAST history response in conversation. Always tell the user
|
|
314
324
|
how many results were returned and whether more pages exist.
|
|
315
325
|
|
|
316
|
-
Chain ids: 0x1 Ethereum \xB7 0xa Optimism \xB7 0x38 BSC \xB7 0x89 Polygon \xB7 0x2105 Base \xB7 0xa4b1 Arbitrum \xB7 0xa86a Avalanche \xB7 0xe708 Linea .`,yn=["get-wallet-token-balances","get-wallet-history","get-wallet-token-transfers","get-wallet-nft-transfers","get-wallet-net-worth","get-wallet-pnl-summary","get-wallet-pnl","get-wallet-approvals","get-wallet-defi-summary","get-wallet-defi-positions","get-wallet-defi-protocol-positions","get-transaction-by-hash","gemini-search-ai"];function jt(c,e,t){return new V({name:"wallet-agent",domain:"wallet",description:["Wallet-scoped on-chain state. Specialised tools cover balances, net worth, transaction history,","single-tx lookup, ERC-20 / NFT transfers, PnL, DeFi positions, and approvals.","For wallet queries without a direct tool , this agent falls","back to a search-grounded Gemini model internally \u2014 it never refuses a wallet-scoped query","and always calls a tool.","NOT in scope: market-wide token data (\u2192 token-agent), NFT current holdings (\u2192 nft-agent)."].join(" "),toolNames:[...yn],systemPrompt:
|
|
326
|
+
Chain ids: 0x1 Ethereum \xB7 0xa Optimism \xB7 0x38 BSC \xB7 0x89 Polygon \xB7 0x2105 Base \xB7 0xa4b1 Arbitrum \xB7 0xa86a Avalanche \xB7 0xe708 Linea .`,yn=["get-wallet-token-balances","get-wallet-history","get-wallet-token-transfers","get-wallet-nft-transfers","get-wallet-net-worth","get-wallet-pnl-summary","get-wallet-pnl","get-wallet-approvals","get-wallet-defi-summary","get-wallet-defi-positions","get-wallet-defi-protocol-positions","get-transaction-by-hash","gemini-search-ai"];function jt(c,e,t){return new V({name:"wallet-agent",domain:"wallet",description:["Wallet-scoped on-chain state. Specialised tools cover balances, net worth, transaction history,","single-tx lookup, ERC-20 / NFT transfers, PnL, DeFi positions, and approvals.","For wallet queries without a direct tool , this agent falls","back to a search-grounded Gemini model internally \u2014 it never refuses a wallet-scoped query","and always calls a tool.","NOT in scope: market-wide token data (\u2192 token-agent), NFT current holdings (\u2192 nft-agent)."].join(" "),toolNames:[...yn],systemPrompt:Ds,cortexFallbackTool:"gemini-search-ai",llm:c,registry:e,options:t})}var Ls=`You are the Token domain expert. You answer market-wide token questions, not wallet-specific ones.
|
|
317
327
|
|
|
318
328
|
SPECIALISED TOOLS (prefer these when intent matches):
|
|
319
329
|
- get-token-info: metadata (name, symbol, decimals, address, logo)
|
|
@@ -354,7 +364,7 @@ GENERAL RULES:
|
|
|
354
364
|
- Never repeat a tool call with identical arguments.
|
|
355
365
|
- Answer in the user's language. Do not mention tool names.
|
|
356
366
|
|
|
357
|
-
Chain ids: 0x1 Ethereum \xB7 0xa Optimism \xB7 0x38 BSC \xB7 0x89 Polygon \xB7 0x2105 Base \xB7 0xa4b1 Arbitrum \xB7 0xa86a Avalanche \xB7 0xe708 Linea. `,bn=["get-token-info","get-token-holders","get-token-analytics","get-token-score","get-trending-tokens","get-top-gainers","gemini-search-ai"];function Yt(c,e,t){return new V({name:"token-agent",domain:"token",description:["Market-wide token data. Specialised tools cover metadata, holders, analytics, risk score,","trending, and top gainers. For token category/theme queries (meme, AI, gaming, L2, RWA),","any token question without a direct tool, this agent falls back to a","search-grounded Gemini model internally \u2014 it never refuses a token query and always calls a tool.","NOT in scope: wallet balances or transfer history (\u2192 wallet-agent), NFT data (\u2192 nft-agent)."].join(" "),toolNames:[...bn],systemPrompt:
|
|
367
|
+
Chain ids: 0x1 Ethereum \xB7 0xa Optimism \xB7 0x38 BSC \xB7 0x89 Polygon \xB7 0x2105 Base \xB7 0xa4b1 Arbitrum \xB7 0xa86a Avalanche \xB7 0xe708 Linea. `,bn=["get-token-info","get-token-holders","get-token-analytics","get-token-score","get-trending-tokens","get-top-gainers","gemini-search-ai"];function Yt(c,e,t){return new V({name:"token-agent",domain:"token",description:["Market-wide token data. Specialised tools cover metadata, holders, analytics, risk score,","trending, and top gainers. For token category/theme queries (meme, AI, gaming, L2, RWA),","any token question without a direct tool, this agent falls back to a","search-grounded Gemini model internally \u2014 it never refuses a token query and always calls a tool.","NOT in scope: wallet balances or transfer history (\u2192 wallet-agent), NFT data (\u2192 nft-agent)."].join(" "),toolNames:[...bn],systemPrompt:Ls,cortexFallbackTool:"gemini-search-ai",llm:c,registry:e,options:t})}var Ms=`You are the NFT domain expert. You answer questions about NFT holdings and metadata, not transfer history.
|
|
358
368
|
|
|
359
369
|
SPECIALISED TOOLS (prefer these when intent matches):
|
|
360
370
|
- get-wallet-nfts: user wants to view / browse their NFTs / holdings \u2014 returns a fixed website notice
|
|
@@ -399,7 +409,7 @@ GENERAL RULES:
|
|
|
399
409
|
- One tool per turn. Never repeat a tool call with identical arguments.
|
|
400
410
|
- Answer in the user's language. Do not mention tool names.
|
|
401
411
|
|
|
402
|
-
Chain ids: 0x1 Ethereum \xB7 0xa Optimism \xB7 0x38 BSC \xB7 0x89 Polygon \xB7 0x2105 Base \xB7 0xa4b1 Arbitrum \xB7 0xa86a Avalanche \xB7 0xe708 Linea.`,wn=["get-wallet-nfts","get-nft-metadata","get-nft-contract-info","send-nft","gemini-search-ai"];function zt(c,e,t){return new V({name:"nft-agent",domain:"nft",description:["NFT holdings, metadata, and sending. Specialised tools cover NFT holdings, single-NFT metadata,","collection / contract info, and sending/transferring an NFT. For NFT category, theme, or market-wide","questions without a direct tool, this agent falls back to a search-grounded Gemini model internally \u2014","it never refuses an NFT query and always calls a tool.",'Handles the SEND / TRANSFER-an-NFT action ("send nft", "g\u1EEDi nft"); but past NFT transfer history (events) \u2192 wallet-agent.',"Key distinction: nft-agent = NFT holdings / details / send; wallet-agent = NFT TRANSFER-EVENT history."].join(" "),toolNames:[...wn],systemPrompt:
|
|
412
|
+
Chain ids: 0x1 Ethereum \xB7 0xa Optimism \xB7 0x38 BSC \xB7 0x89 Polygon \xB7 0x2105 Base \xB7 0xa4b1 Arbitrum \xB7 0xa86a Avalanche \xB7 0xe708 Linea.`,wn=["get-wallet-nfts","get-nft-metadata","get-nft-contract-info","send-nft","gemini-search-ai"];function zt(c,e,t){return new V({name:"nft-agent",domain:"nft",description:["NFT holdings, metadata, and sending. Specialised tools cover NFT holdings, single-NFT metadata,","collection / contract info, and sending/transferring an NFT. For NFT category, theme, or market-wide","questions without a direct tool, this agent falls back to a search-grounded Gemini model internally \u2014","it never refuses an NFT query and always calls a tool.",'Handles the SEND / TRANSFER-an-NFT action ("send nft", "g\u1EEDi nft"); but past NFT transfer history (events) \u2192 wallet-agent.',"Key distinction: nft-agent = NFT holdings / details / send; wallet-agent = NFT TRANSFER-EVENT history."].join(" "),toolNames:[...wn],systemPrompt:Ms,cortexFallbackTool:"gemini-search-ai",llm:c,registry:e,options:t})}var Bs=`You are the Crypto AI fallback expert powered by a search-grounded Gemini model.
|
|
403
413
|
|
|
404
414
|
You answer ANY blockchain / crypto / token / NFT / DeFi / on-chain question that does not have a
|
|
405
415
|
direct structured tool in the other agents. The underlying model has live web-search grounding \u2014
|
|
@@ -418,7 +428,7 @@ ROUTING:
|
|
|
418
428
|
RULES:
|
|
419
429
|
- ALWAYS call the tool \u2014 NEVER refuse, NEVER answer without calling it.
|
|
420
430
|
- NEVER retry with the same prompt if the call fails \u2014 return what you have.
|
|
421
|
-
- Answer in the user's language. Do NOT mention tool names.`,kn=["gemini-search-ai"];function Qt(c,e,t){return new V({name:"ai-agent",domain:"ai",description:["Catch-all crypto / blockchain expert powered by a search-grounded Gemini model (live Google","Search). USE THIS AGENT whenever the query is about blockchain, crypto, tokens, NFTs, DeFi,","wallets, on-chain data, smart contracts, or any supported EVM chain BUT no other agent","(wallet-agent, token-agent, nft-agent, pool-agent) has a direct structured tool for it.","Scope examples: (1) token categories or themes \u2014 meme tokens, AI tokens, gaming tokens, L2 tokens, RWA;",'(2) protocol-specific questions \u2014 "how does X protocol work", "what happened to Y";',"(3) cross-contract or exploratory EVM analysis spanning many contracts;","(4) any open-ended on-chain question without a direct structured tool.","Prefer the specific agents when they clearly match (wallet balances \u2192 wallet-agent,","single-token info/analytics \u2192 token-agent, NFT holdings/metadata \u2192 nft-agent, DEX pools \u2192 pool-agent).","But for anything crypto-related that falls outside those, route HERE \u2014 do not return empty."].join(" "),toolNames:[...kn],systemPrompt:
|
|
431
|
+
- Answer in the user's language. Do NOT mention tool names.`,kn=["gemini-search-ai"];function Qt(c,e,t){return new V({name:"ai-agent",domain:"ai",description:["Catch-all crypto / blockchain expert powered by a search-grounded Gemini model (live Google","Search). USE THIS AGENT whenever the query is about blockchain, crypto, tokens, NFTs, DeFi,","wallets, on-chain data, smart contracts, or any supported EVM chain BUT no other agent","(wallet-agent, token-agent, nft-agent, pool-agent) has a direct structured tool for it.","Scope examples: (1) token categories or themes \u2014 meme tokens, AI tokens, gaming tokens, L2 tokens, RWA;",'(2) protocol-specific questions \u2014 "how does X protocol work", "what happened to Y";',"(3) cross-contract or exploratory EVM analysis spanning many contracts;","(4) any open-ended on-chain question without a direct structured tool.","Prefer the specific agents when they clearly match (wallet balances \u2192 wallet-agent,","single-token info/analytics \u2192 token-agent, NFT holdings/metadata \u2192 nft-agent, DEX pools \u2192 pool-agent).","But for anything crypto-related that falls outside those, route HERE \u2014 do not return empty."].join(" "),toolNames:[...kn],systemPrompt:Bs,cortexFallbackTool:"gemini-search-ai",llm:c,registry:e,options:t})}var Os=`You are the Pool / DEX Liquidity domain expert.
|
|
422
432
|
|
|
423
433
|
You answer questions about decentralized exchange liquidity pools \u2014 specifically Uniswap V2/V3/V4 pools across EVM chains \u2014 AND you guide the user through providing liquidity to a pool.
|
|
424
434
|
|
|
@@ -559,7 +569,7 @@ State awareness:
|
|
|
559
569
|
|
|
560
570
|
Never fabricate pool addresses, tick numbers, prices, or NFT ids. Every value must come from a tool call this turn or the immediately preceding turn.
|
|
561
571
|
|
|
562
|
-
Chain ids: 0x1 Ethereum \xB7 0xa Optimism \xB7 0x38 BSC \xB7 0x89 Polygon \xB7 0x2105 Base \xB7 0xa4b1 Arbitrum \xB7 0xa86a Avalanche \xB7 0xe708 Linea .`,Tn=["get-top-pools","get-pool-detail","search-pools","lookup-pool-by-address","open-add-liquidity-form","preview-add-liquidity","estimate_pool_yield"];function Xt(c,e,t){return new V({name:"pool-agent",domain:"pool",description:["Handles questions about decentralized exchange liquidity pools \u2014 Uniswap V2/V3/V4 across EVM chains \u2014","AND walks the user through ADDING LIQUIDITY to a pool (provide LP / farm / stake / deposit / cung c\u1EA5p thanh kho\u1EA3n).","Scope: (1) top pools by chain \u2014 biggest pools ranked by TVL, volume, or transaction count;","(2) pool composition \u2014 which token pairs dominate liquidity on a given network;","(3) pool-level analytics \u2014 fee tier, rolling volume buckets (1d/1w/30d), tx count, both tokens;","(4) pool detail by token pair (and optional fee tier) \u2014 full stats for a single pool including TVL, 24h volume, fees, APR estimate, token reserves, and weekly volume history;","(5) pool search \u2014 find pools that match a free-text query (token symbol, token name, or pool address) on a given chain;","(6) single-address lookup \u2014 when the user pastes one 0x address without saying whether it is a pool or a token, resolve both;",'(7) YIELD ESTIMATION \u2014 estimate daily/weekly/monthly/yearly fee income for a given USD deposit into a specific pool ("if I put $1000 into USDC/WETH 0.05%, how much do I earn per day?", "b\u1ECF 500$ v\xE0o pool n\xE0y l\u1EDDi bao nhi\xEAu?");',"(8) ADD LIQUIDITY \u2014 open the AddLiquidityForm UI for a chosen pool, then build the unsigned mint transaction once the user confirms amount and price range.","NOT in scope: wallet-specific LP positions or DeFi holdings the user already owns (\u2192 wallet-agent),","token metadata or token-level market data (\u2192 token-agent), NFT data (\u2192 nft-agent).",'Key distinction from token-agent: pool-agent answers "which POOLS are biggest / most active" and handles "I want to add LP",','while token-agent answers "what is this TOKEN" and "what is the market doing for this TOKEN".'].join(" "),toolNames:[...Tn],systemPrompt:
|
|
572
|
+
Chain ids: 0x1 Ethereum \xB7 0xa Optimism \xB7 0x38 BSC \xB7 0x89 Polygon \xB7 0x2105 Base \xB7 0xa4b1 Arbitrum \xB7 0xa86a Avalanche \xB7 0xe708 Linea .`,Tn=["get-top-pools","get-pool-detail","search-pools","lookup-pool-by-address","open-add-liquidity-form","preview-add-liquidity","estimate_pool_yield"];function Xt(c,e,t){return new V({name:"pool-agent",domain:"pool",description:["Handles questions about decentralized exchange liquidity pools \u2014 Uniswap V2/V3/V4 across EVM chains \u2014","AND walks the user through ADDING LIQUIDITY to a pool (provide LP / farm / stake / deposit / cung c\u1EA5p thanh kho\u1EA3n).","Scope: (1) top pools by chain \u2014 biggest pools ranked by TVL, volume, or transaction count;","(2) pool composition \u2014 which token pairs dominate liquidity on a given network;","(3) pool-level analytics \u2014 fee tier, rolling volume buckets (1d/1w/30d), tx count, both tokens;","(4) pool detail by token pair (and optional fee tier) \u2014 full stats for a single pool including TVL, 24h volume, fees, APR estimate, token reserves, and weekly volume history;","(5) pool search \u2014 find pools that match a free-text query (token symbol, token name, or pool address) on a given chain;","(6) single-address lookup \u2014 when the user pastes one 0x address without saying whether it is a pool or a token, resolve both;",'(7) YIELD ESTIMATION \u2014 estimate daily/weekly/monthly/yearly fee income for a given USD deposit into a specific pool ("if I put $1000 into USDC/WETH 0.05%, how much do I earn per day?", "b\u1ECF 500$ v\xE0o pool n\xE0y l\u1EDDi bao nhi\xEAu?");',"(8) ADD LIQUIDITY \u2014 open the AddLiquidityForm UI for a chosen pool, then build the unsigned mint transaction once the user confirms amount and price range.","NOT in scope: wallet-specific LP positions or DeFi holdings the user already owns (\u2192 wallet-agent),","token metadata or token-level market data (\u2192 token-agent), NFT data (\u2192 nft-agent).",'Key distinction from token-agent: pool-agent answers "which POOLS are biggest / most active" and handles "I want to add LP",','while token-agent answers "what is this TOKEN" and "what is the market doing for this TOKEN".'].join(" "),toolNames:[...Tn],systemPrompt:Os,llm:c,registry:e,options:t})}var Do=`You are the Uniswap V3 Subgraph DEX-pool expert.
|
|
563
573
|
|
|
564
574
|
You answer questions about Uniswap V3 liquidity pools using The Graph subgraphs. APR values are enriched from DefiLlama Yields. Concentrated-liquidity price-range data comes from the CoinPool platform.
|
|
565
575
|
|
|
@@ -621,10 +631,10 @@ COINPOOL RANGE GUIDANCE (when using subgraph-coinpool-pairs):
|
|
|
621
631
|
FOLLOW-UPS / CONTINUATIONS:
|
|
622
632
|
- Short replies like "yes", "tell me more", "the first one", "show ranges" refer back to the previous turn. Extract tokens/chains/pool addresses from the prior assistant message and call the appropriate tool. NEVER ask for clarification when history clearly shows what was discussed.
|
|
623
633
|
|
|
624
|
-
NEVER fabricate pool addresses, token prices, APRs, or position ids. Every value must come from a tool call this turn or the immediately preceding turn.`;function
|
|
634
|
+
NEVER fabricate pool addresses, token prices, APRs, or position ids. Every value must come from a tool call this turn or the immediately preceding turn.`;function $s(c){return c?`${Do}
|
|
625
635
|
|
|
626
636
|
ADD-LIQUIDITY LINK:
|
|
627
|
-
- If the user mentions adding liquidity / adding a pool / adding a position (including phrases like "add liquidity", "add pool", "add position", "create pool", "create pair", "th\xEAm thanh kho\u1EA3n", "th\xEAm pool", "th\xEAm position"), keep your normal answer content and append this exact link at the end of the response: ${c}`:Do}var Lo=["subgraph-search-pools","subgraph-trending-pools","subgraph-pool-by-address","subgraph-pool-by-position-id","subgraph-position-detail","subgraph-coinpool-pairs"];function Wn(c,e,t){return new V({name:"pool-subgraph-agent",domain:"pool",description:["Answers questions about Uniswap V3 liquidity pools using The Graph subgraphs as the primary source","(APR is enriched from DefiLlama Yields; concentrated-liquidity price ranges come from CoinPool).","Scope: (1) pool search by token symbols on one or more EVM chains, with TVL/APR/fee filters and sort by tvl/volume/apr/fee/liquidity;","(2) trending pools ranked by 24h volume on a chain;","(3) pool detail for a specific 0x pool address;","(4) lookup the underlying pool for a Uniswap V3 NFT position id (numeric, e.g. 962961);","(5) full position detail (deposits, withdrawals, collected fees) for a numeric position id;","(6) CoinPool concentrated-liquidity price-range candidates with per-range APR for a token pair.",'NOT in scope: wallet-wide LP holdings ("my positions"), sending/transferring LP NFTs, add-liquidity action flows,',"token metadata (\u2192 token-agent), NFT data (\u2192 nft-agent).","Prefer this subagent over pool-agent when the deployment is configured to use Subgraph data (the two are mutually exclusive at config time)."].join(" "),toolNames:[...Lo],systemPrompt
|
|
637
|
+
- If the user mentions adding liquidity / adding a pool / adding a position (including phrases like "add liquidity", "add pool", "add position", "create pool", "create pair", "th\xEAm thanh kho\u1EA3n", "th\xEAm pool", "th\xEAm position"), keep your normal answer content and append this exact link at the end of the response: ${c}`:Do}var Lo=["subgraph-search-pools","subgraph-trending-pools","subgraph-pool-by-address","subgraph-pool-by-position-id","subgraph-position-detail","subgraph-coinpool-pairs"];function Wn(c,e,t){return new V({name:"pool-subgraph-agent",domain:"pool",description:["Answers questions about Uniswap V3 liquidity pools using The Graph subgraphs as the primary source","(APR is enriched from DefiLlama Yields; concentrated-liquidity price ranges come from CoinPool).","Scope: (1) pool search by token symbols on one or more EVM chains, with TVL/APR/fee filters and sort by tvl/volume/apr/fee/liquidity;","(2) trending pools ranked by 24h volume on a chain;","(3) pool detail for a specific 0x pool address;","(4) lookup the underlying pool for a Uniswap V3 NFT position id (numeric, e.g. 962961);","(5) full position detail (deposits, withdrawals, collected fees) for a numeric position id;","(6) CoinPool concentrated-liquidity price-range candidates with per-range APR for a token pair.",'NOT in scope: wallet-wide LP holdings ("my positions"), sending/transferring LP NFTs, add-liquidity action flows,',"token metadata (\u2192 token-agent), NFT data (\u2192 nft-agent).","Prefer this subagent over pool-agent when the deployment is configured to use Subgraph data (the two are mutually exclusive at config time)."].join(" "),toolNames:[...Lo],systemPrompt:$s(t?.linkAddLiquidity),llm:c,registry:e,options:t})}var qs=`Call exactly ONE tool every turn \u2014 ALWAYS, even for a bare verb with no details ("send token", "buy", "approve"). Never reply in text and never ask the user for missing fields (token, amount, recipient): the tool opens a picker/form that collects them.
|
|
628
638
|
|
|
629
639
|
Tools:
|
|
630
640
|
- open-send-native-form \u2014 native coin (ETH/BNB/MATIC/\u2026)
|
|
@@ -645,7 +655,7 @@ NFT sends are NOT handled here \u2014 they belong to the nft-agent.
|
|
|
645
655
|
|
|
646
656
|
Skip the tool ONLY if: walletAddress missing (ask to connect) \xB7 user explicitly names a chain different from userContext.chain (ask to switch).
|
|
647
657
|
|
|
648
|
-
After the tool returns: 1 short sentence in the user's language. Don't list missing fields. Don't mention tool names.`,vn=["open-send-native-form","open-send-token-form","open-buy-token-form","open-swap-token-form","open-approve-token-form"];function Jt(c,e,t){return new V({name:"wallet-action-agent",domain:"wallet-action",description:["User wants to PERFORM an on-chain action (send, buy, swap, approve).","This agent picks the matching open-*-form tool which emits a pre-filled form (or, for buy/swap, a confirm panel) for the FE to render.","The FE \u2014 not this agent \u2014 builds and submits the tx after the user confirms.","NOT in scope: read-only wallet/token/NFT queries, sending/transferring an NFT (\u2192 nft-agent), liquidity provisioning."].join(" "),toolNames:[...vn],systemPrompt
|
|
658
|
+
After the tool returns: 1 short sentence in the user's language. Don't list missing fields. Don't mention tool names.`,vn=["open-send-native-form","open-send-token-form","open-buy-token-form","open-swap-token-form","open-approve-token-form"];function Jt(c,e,t){return new V({name:"wallet-action-agent",domain:"wallet-action",description:["User wants to PERFORM an on-chain action (send, buy, swap, approve).","This agent picks the matching open-*-form tool which emits a pre-filled form (or, for buy/swap, a confirm panel) for the FE to render.","The FE \u2014 not this agent \u2014 builds and submits the tx after the user confirms.","NOT in scope: read-only wallet/token/NFT queries, sending/transferring an NFT (\u2192 nft-agent), liquidity provisioning."].join(" "),toolNames:[...vn],systemPrompt:qs,mustCallTool:!0,llm:c,registry:e,options:t})}function Sn(c,e,t,n){let r=(l,u)=>{let d=n?.[l];return d===void 0?u:d},o=r("pool-subgraph",!1),s=n?.pool===!0,a=o?s:r("pool",!0),i=[];return r("wallet",!0)&&i.push(jt(c,e,t)),r("wallet-action",!0)&&i.push(Jt(c,e,t)),r("token",!0)&&i.push(Yt(c,e,t)),r("nft",!0)&&i.push(zt(c,e,t)),a&&i.push(Xt(c,e,t)),o&&i.push(Wn(c,e,t)),r("ai",!0)&&i.push(Qt(c,e,t)),i}var Fs=`You are a response synthesizer. Given the user's question and tool execution results, produce a clear, helpful final answer.
|
|
649
659
|
|
|
650
660
|
Rules:
|
|
651
661
|
- Be concise but complete.
|
|
@@ -655,11 +665,11 @@ Rules:
|
|
|
655
665
|
- Respond in the same language as the user's message.
|
|
656
666
|
- Do NOT mention internal tool names, JSON structures, or technical implementation details.
|
|
657
667
|
`,Mt=class{llm;constructor(e){this.llm=e}async synthesise(e,t,n,r){if(!t.steps.some(p=>p.phase==="act")&&t.finalAnswer)return Mo(t.finalAnswer);let s=t.steps.filter(p=>p.phase==="observe").map((p,h)=>`[Result ${h+1}]: ${p.content}`).join(`
|
|
658
|
-
`),a=de(n),i=ye(a,8),l=(r||"").trim().toLowerCase(),u=l?`IMPORTANT: Reply in BCP-47 language "${l}" (the language the user actually typed). Tool results may contain text in other languages \u2014 ignore that and reply ONLY in "${l}".`:`IMPORTANT: The user wrote in the language of this message: "${e}". Reply in that exact language, ignoring any other languages that appear inside tool results.`,d=[{role:"system",content:
|
|
668
|
+
`),a=de(n),i=ye(a,8),l=(r||"").trim().toLowerCase(),u=l?`IMPORTANT: Reply in BCP-47 language "${l}" (the language the user actually typed). Tool results may contain text in other languages \u2014 ignore that and reply ONLY in "${l}".`:`IMPORTANT: The user wrote in the language of this message: "${e}". Reply in that exact language, ignoring any other languages that appear inside tool results.`,d=[{role:"system",content:Fs,timestamp:0},...i,{role:"user",content:[`User question: ${e}`,"",`Agent reasoning: ${t.finalAnswer}`,"",s?`Tool results:
|
|
659
669
|
${s}`:"","",u].filter(Boolean).join(`
|
|
660
670
|
`),timestamp:Date.now()}],m=await this.llm.chat(d);return Mo(m.text)}};function Mo(c){return c&&c.replace(/^\[[a-z0-9-]+-agent\]\s*/i,"").replace(/\n\n\[[a-z0-9-]+-agent\]\s*/gi,`
|
|
661
671
|
|
|
662
|
-
`)}var
|
|
672
|
+
`)}var Ws=`You are a CONTEXTUALIZER.
|
|
663
673
|
|
|
664
674
|
Your ONLY job: read the conversation history + the user's latest message,
|
|
665
675
|
and produce a SELF-CONTAINED version of that message.
|
|
@@ -781,7 +791,7 @@ When the user references "this transaction", "that tx", "the transaction above",
|
|
|
781
791
|
- Scan the conversation history for tool results (role: "tool") that contain transaction data.
|
|
782
792
|
- Extract the EXACT hash value (starts with "0x", 66 chars) from the most recent such result.
|
|
783
793
|
- Include it verbatim in rewrittenQuery. NEVER invent or modify a hash.
|
|
784
|
-
- If no hash is found in history, set rewrittenQuery to the original message verbatim so the subagent knows to fetch it.`,
|
|
794
|
+
- If no hash is found in history, set rewrittenQuery to the original message verbatim so the subagent knows to fetch it.`,Vs=`You detect the language of a single user message.
|
|
785
795
|
|
|
786
796
|
Output STRICT JSON, no prose, no code fences:
|
|
787
797
|
{"language": "<BCP-47 tag>"}
|
|
@@ -813,11 +823,11 @@ Examples:
|
|
|
813
823
|
|
|
814
824
|
${i.join(`
|
|
815
825
|
|
|
816
|
-
`)}`:"",u=Date.now(),d=[{role:"system",content:
|
|
826
|
+
`)}`:"",u=Date.now(),d=[{role:"system",content:Ws,timestamp:0},...a,{role:"user",content:`Latest user message to contextualize:
|
|
817
827
|
${e}${l}
|
|
818
828
|
|
|
819
|
-
Return JSON only.`,timestamp:Date.now()}],m=[{role:"system",content:
|
|
820
|
-
[QueryRewriter] (${k}ms)`),console.log(` original : "${e}"`),console.log(` rewritten : "${y.rewrittenQuery}"${w?"":" (unchanged)"}`),console.log(` followUp : ${y.isFollowUp} topicChanged: ${y.topicChanged}`),y.reasoning&&console.log(` reasoning : ${y.reasoning}`),console.log(` language : ${y.language}`),console.log(` webSearch : ${y.needsWebSearch}`)}return y}parseJSON(e){let t=e.trim();t.startsWith("```")&&(t=t.replace(/^```(?:json)?\s*/,"").replace(/```\s*$/,""));let n=t.match(/\{[\s\S]*\}/);return n&&(t=n[0]),JSON.parse(t)}};var N=require("@langchain/langgraph");var Z=c=>({reducer:(e,t)=>t,default:c}),Vs=N.Annotation.Root({userMessage:(0,N.Annotation)(),doSuggest:(0,N.Annotation)(Z(()=>!0)),overrideLang:(0,N.Annotation)(Z(()=>"")),conversationMessages:(0,N.Annotation)(Z(()=>[])),lastSubagents:(0,N.Annotation)(Z(()=>[])),messageId:(0,N.Annotation)(Z(()=>"")),rewrite:(0,N.Annotation)(Z(()=>null)),effectiveQuery:(0,N.Annotation)(Z(()=>"")),turnLanguage:(0,N.Annotation)(Z(()=>"")),kbContext:(0,N.Annotation)(Z(()=>"")),decision:(0,N.Annotation)(Z(()=>null)),assignment:(0,N.Annotation)(Z(()=>null)),assignmentIndex:(0,N.Annotation)(Z(()=>0)),subResults:(0,N.Annotation)({reducer:(c,e)=>c.concat(e),default:()=>[]}),response:(0,N.Annotation)(Z(()=>null))});function Bo(c){let e=async d=>{c.addUserMessage(d.userMessage),c.needsSummary()&&await c.compactHistory();let m=c.conversationForTurn(),p=c.lastAssistantSubagents(m);return c.debug&&p.length>0&&console.log(`[AgentCore] previous turn handled by: ${p.join(", ")}`),{conversationMessages:m,lastSubagents:p,messageId:c.generateMessageId()}},t=async d=>{let m=await c.rewrite(d.userMessage,d.conversationMessages,d.lastSubagents),p=d.overrideLang||m.language||"";return{rewrite:m,effectiveQuery:m.rewrittenQuery,turnLanguage:p}},n=async d=>{let m=d.rewrite,p=m.isFollowUp?d.effectiveQuery:d.userMessage,h=await c.searchKB(p),f=c.formatKBContext(h),g=await c.tryAnswerFromKB(d.userMessage,h,f,d.messageId);return g?{kbContext:f,response:{...g,messageId:d.messageId,rewrite:m}}:{kbContext:f}},r=async d=>({decision:await c.route(d.effectiveQuery,d.conversationMessages,c.subagentCards(),d.lastSubagents)}),o=async d=>({response:{...await c.answerDirectly(d.effectiveQuery,d.conversationMessages,d.kbContext,d.doSuggest,d.messageId,d.rewrite?.needsWebSearch??!1),messageId:d.messageId,routerDecision:d.decision,rewrite:d.rewrite}}),s=async d=>{let m=await c.runSubagent(d.assignment,d.conversationMessages,d.turnLanguage);return{subResults:[{i:d.assignmentIndex,result:m}]}},a=async d=>{let m=d.subResults.slice().sort((b,T)=>b.i-T.i).map(b=>b.result);c.persistToolMessages(m);let p=c.mergeTrace(m),h=await c.synthesise(d.userMessage,p,c.synthesiserHistory(),d.turnLanguage||void 0),f=m.map(b=>b.subagentName),g=c.collectUiActions(m,d.turnLanguage||null),y=c.collectActionButtons(m,d.turnLanguage||null),k=c.subResultsTriggerNoSuggestions(m);return{response:{...await c.finaliseAnswer({userMessage:d.userMessage,answer:h,subagents:f,uiActions:g,actionButtons:y,messageId:d.messageId},{generateSuggestions:d.doSuggest&&!k&&y.length===0}),messageId:d.messageId,trace:p,routerDecision:d.decision,subagentResults:m,rewrite:d.rewrite,...g.length>0?{uiActions:g}:{},...y.length>0?{actionButtons:y}:{}}}},i=async d=>{let m=(d.rewrite?.mentionedChain||String(c.connectedChain()??"")).trim();return{response:{...await c.answerUnsupportedChain(m,d.turnLanguage,d.messageId),messageId:d.messageId,routerDecision:d.decision,rewrite:d.rewrite}}},l=d=>d.response?N.END:"route",u=d=>{let m=d.decision?.assignments??[];if(m.length===0)return"direct";let p=m.some(f=>f.subagent!=="ai-agent"),h=(d.rewrite?.mentionedChain||c.connectedChain()||"").trim();return p&&h!==""&&!jn(h)?"unsupportedChain":m.map((f,g)=>new N.Send("subagent",{...d,assignment:f,assignmentIndex:g}))};return new N.StateGraph(Vs).addNode("setup",e).addNode("contextualize",t).addNode("kb",n).addNode("route",r).addNode("direct",o).addNode("subagent",s).addNode("synthesize",a).addNode("unsupportedChain",i).addEdge(N.START,"setup").addEdge("setup","contextualize").addEdge("contextualize","kb").addConditionalEdges("kb",l,["route",N.END]).addConditionalEdges("route",u,["direct","subagent","unsupportedChain"]).addEdge("unsupportedChain",N.END).addEdge("direct",N.END).addEdge("subagent","synthesize").addEdge("synthesize",N.END).compile()}var Oo=require("js-sha3");function Pn(){let c=Date.now().toString(16),e=Math.floor(Math.random()*65535).toString(16).padStart(4,"0");return(c+e).slice(-8)}var Hs="You are a helpful AI assistant. Answer the user accurately and concisely. Respond in the same language as the user.",An=class{llm;registry;history;summarizer;router;rewriter;subagents;synthesizer;chatGraph;systemPrompt;debug;secretKey;licenseChecked=!1;userContext={};storage;storageKey;persistHistoryEnabled;kb;kbEntries;vectorKB=null;vectorKBAutoIngested=!1;vectorKBAutoIngestPromise=null;autoIngestEnabled=!1;kbAnswerThreshold;suggestionsEnabled;historyLoaded=!1;historyLoadingPromise=null;historyReady;constructor(e){this.secretKey=e.secretKey,$n(e.rpcUrls),this.llm=new le(e.llm),this.registry=new Ee,this.history=new Nt(e.maxHistoryMessages??50),this.summarizer=new It(this.llm),this.synthesizer=new Mt(this.llm),this.systemPrompt=e.systemPrompt??Hs,this.debug=e.debug??!1,this.suggestionsEnabled=e.generateSuggestions??!0,this.storage=e.storage??null,this.storageKey=e.storageKey??"keyring-agent-history",this.persistHistoryEnabled=e.persistHistory??!1,this.kbEntries=new Dt(e.knowledgeBase),this.kbEntries.setLLM(this.llm);let t=e.vectorKB;t?.enabled?(t.provider?this.kb=t.provider:(this.vectorKB=new Se({url:t.url,token:t.token,namespace:t.namespace,minScore:t.minScore,defaultTopK:t.defaultTopK,debug:this.debug}),this.kb=this.vectorKB,this.autoIngestEnabled=t.autoIngest===!0),this.debug&&console.log(`[AgentCore] vector KB enabled (${t.provider?"custom provider":"Upstash"}`+(t.namespace?`, ns=${t.namespace}`:"")+")")):this.kb=this.kbEntries;let n=e.kbAnswerThreshold;if(typeof n=="number"?this.kbAnswerThreshold=n:t?.enabled&&!t.provider?this.kbAnswerThreshold=t.minScore??1.3:this.kbAnswerThreshold=.8,this.debug&&console.log(`[AgentCore] kbAnswerThreshold = ${this.kbAnswerThreshold}`),e.moralis!==!1){let u=typeof e.moralis=="object"?e.moralis:void 0;this.registry.register(new Fe(u)),this.registry.register(new Le(u)),this.registry.register(new We(u)),this.registry.register(new Ve(u)),this.registry.register(new He(u)),this.registry.register(new Me(u)),this.registry.register(new Ge(u)),this.registry.register(new Ke(u)),this.registry.register(new Xe(u)),this.registry.register(new Ye(u)),this.registry.register(new ze(u)),this.registry.register(new Qe(u)),this.registry.register(new je(u)),this.registry.register(new $e(u)),this.registry.register(new qe(u)),this.registry.register(new Be(u)),this.registry.register(new Oe(u))}let r=e.nftLink;this.registry.register(new Je({url:r})),this.registry.register(new Ze({url:r})),this.registry.register(new et({url:r})),this.registry.register(new tt({url:r})),this.registry.register(new nt(e.llm));let o=e.subagents?.["pool-subgraph"]===!0;if((o?e.subagents?.pool===!0:e.subagents?.pool!==!1)&&e.uniswap!==!1){let u=typeof e.uniswap=="object"?e.uniswap:void 0;this.registry.register(new ot(u)),this.registry.register(new rt(u)),this.registry.register(new st(u)),this.registry.register(new at(u)),this.registry.register(new ft(u));let d=u?{isProduction:u.isProduction}:void 0;this.registry.register(new ht({baseUrl:u?.baseUrl,pool:d,minProvideUsd:u?.minProvideUsd})),this.registry.register(new gt({pool:d}))}if(o&&e.subgraph!==!1){let u=typeof e.subgraph=="object"?e.subgraph:void 0;this.registry.register(new yt(u)),this.registry.register(new bt(u)),this.registry.register(new wt(u)),this.registry.register(new kt(u)),this.registry.register(new Tt(u)),this.registry.register(new vt(u))}let a=e.moralis===!1?void 0:typeof e.moralis=="object"?e.moralis:{};this.registry.register(new Pt(a)),this.registry.register(new At(a)),this.registry.register(new _t(a)),this.registry.register(new Ct(a)),this.registry.register(new Ut(a)),this.registry.register(new Rt(a,{debug:this.debug})),this.registry.register(new Et(a,{debug:this.debug})),this.router=new Lt(this.llm,{debug:this.debug}),this.rewriter=new xn(this.llm,{debug:this.debug});let i=typeof e.subgraph=="object"?e.subgraph:void 0,l=Sn(this.llm,this.registry,{maxToolCalls:e.maxIterations??5,debug:this.debug,linkAddLiquidity:i?.linkAddLiquidity},e.subagents);this.subagents=new Map(l.map(u=>[u.name,u])),this.chatGraph=Bo(this.createGraphHost()),this.historyReady=this.loadHistory()}registerTool(e){this.registry.register(e)}registerTools(e){for(let t of e)this.registry.register(t)}unregisterTool(e){return this.registry.unregister(e)}listTools(){return this.registry.listNames()}async invokeTool(e,t,n){if(this.assertLicense(),await this.loadHistory(),!this.registry.get(e))return{answer:`Unknown tool: "${e}".`,messageId:Pn()};this.debug&&console.log(`[AgentCore] invokeTool ${e} ${JSON.stringify(t)}`);let o=n?.userMessage??`[invoke ${e}]`;this.history.add({role:"user",content:o,timestamp:Date.now()});let s=await this.registry.execute(e,t,this.userContext),a=s.data,i=s.success?a?.summary??"Done.":`Action failed: ${s.error??"unknown error"}`,l=Pn(),u=s.success&&s.ui?[this.stampLanguage(s.ui,null)]:[],d={role:"assistant",content:i,timestamp:Date.now(),subagents:["direct-invoke"],messageId:l};return u.length>0&&(d.uiActions=u),this.history.add(d),await this.persistHistory(),{answer:i,messageId:l,...u.length>0?{uiActions:u}:{}}}registerSubagent(e){this.subagents.set(e.name,e)}unregisterSubagent(e){return this.subagents.delete(e)}listSubagents(){return Array.from(this.subagents.keys())}setKnowledgeBase(e){this.kbEntries.setEntries(e),this.vectorKBAutoIngested=!1}addKnowledgeBase(e){this.kbEntries.addEntries(e),this.vectorKBAutoIngested=!1}async ingestKnowledgeBase(){if(!this.vectorKB)return{count:0};let e=this.kbEntries.getEntries();if(e.length===0)return{count:0};let t=await this.vectorKB.upsert(e);return this.vectorKBAutoIngested=!0,t}async maybeAutoIngest(e){if(!(!e||!this.vectorKB||this.vectorKBAutoIngested)){if(this.vectorKBAutoIngestPromise)return this.vectorKBAutoIngestPromise;this.vectorKBAutoIngestPromise=(async()=>{try{let{count:t}=await this.ingestKnowledgeBase();this.debug&&t>0&&console.log(`[AgentCore] auto-ingested ${t} KB entr${t===1?"y":"ies"} to vector store`)}catch(t){this.debug&&console.warn("[AgentCore] auto-ingest failed:",t)}finally{this.vectorKBAutoIngestPromise=null}})(),await this.vectorKBAutoIngestPromise}}setUserContext(e){this.userContext={...e}}getUserContext(){return{...this.userContext}}async loadHistory(){if(!this.historyLoaded){if(this.historyLoadingPromise)return this.historyLoadingPromise;this.historyLoadingPromise=this._doLoadHistory(),await this.historyLoadingPromise}}async _doLoadHistory(){if(!this.storage){this.debug&&console.log("[AgentCore] loadHistory: no storage configured"),this.historyLoaded=!0;return}if(!this.persistHistoryEnabled){this.debug&&console.log("[AgentCore] loadHistory: persistHistory disabled \u2014 starting fresh session");try{await this.storage.removeItem(this.storageKey)}catch(e){this.debug&&console.warn("[AgentCore] failed to clear stale history:",e)}this.historyLoaded=!0;return}try{let e=await this.storage.getItem(this.storageKey);if(this.debug&&console.log(`[AgentCore] loadHistory: storage key "${this.storageKey}" ${e?`has ${e.length} chars`:"is empty"}`),e){let t=JSON.parse(e);this.history.restore(t),this.debug&&console.log(`[AgentCore] Restored ${this.history.length} messages from storage`)}}catch(e){this.debug&&console.warn("[AgentCore] Failed to load history from storage:",e)}finally{this.historyLoaded=!0}}async persistHistory(){if(this.storage&&this.persistHistoryEnabled)try{let e=JSON.stringify(this.history.serialise());await this.storage.setItem(this.storageKey,e)}catch(e){this.debug&&console.warn("[AgentCore] Failed to persist history to storage:",e)}}assertLicense(){if(this.licenseChecked)return;let e="d00d67291d3c660a5034b01c0b2afbc8d4ac5552099a04f28a92d75fdb44c188";if(e&&(!this.secretKey||(0,Oo.sha3_256)(this.secretKey)!==e))throw new Error("AgentCore: invalid or missing secretKey.");this.licenseChecked=!0}async chat(e,t){this.assertLicense();let n=t?.generateSuggestions??this.suggestionsEnabled,r=typeof t?.language=="string"&&t.language.trim()?t.language.trim():"";await this.loadHistory(),await this.maybeAutoIngest(this.autoIngestEnabled);let o=Date.now();this.debug&&(console.log(`
|
|
829
|
+
Return JSON only.`,timestamp:Date.now()}],m=[{role:"system",content:Vs,timestamp:0},{role:"user",content:e,timestamp:Date.now()}],[p,h]=await Promise.allSettled([this.llm.chat(d),this.llm.chat(m)]),f={};if(p.status==="fulfilled")try{f=this.parseJSON(p.value.text)}catch(k){this.debug&&console.log(`[QueryRewriter] rewrite parse fail: ${k}`)}else this.debug&&console.log(`[QueryRewriter] rewrite call fail: ${p.reason}`);let g="";if(h.status==="fulfilled")try{let k=this.parseJSON(h.value.text);typeof k.language=="string"&&(g=k.language.trim().toLowerCase())}catch(k){this.debug&&console.log(`[QueryRewriter] detect parse fail: ${k}`)}else this.debug&&console.log(`[QueryRewriter] detect call fail: ${h.reason}`);let y={isFollowUp:r&&typeof f.isFollowUp=="boolean"?f.isFollowUp:!1,rewrittenQuery:typeof f.rewrittenQuery=="string"&&f.rewrittenQuery.trim()?f.rewrittenQuery.trim():e,topicChanged:r&&typeof f.topicChanged=="boolean"?f.topicChanged:!1,reasoning:typeof f.reasoning=="string"?f.reasoning:"",language:g,mentionedChain:typeof f.mentionedChain=="string"?f.mentionedChain.trim():"",needsWebSearch:f.needsWebSearch===!0};if(this.debug){let k=Date.now()-u,w=y.rewrittenQuery!==e;console.log(`
|
|
830
|
+
[QueryRewriter] (${k}ms)`),console.log(` original : "${e}"`),console.log(` rewritten : "${y.rewrittenQuery}"${w?"":" (unchanged)"}`),console.log(` followUp : ${y.isFollowUp} topicChanged: ${y.topicChanged}`),y.reasoning&&console.log(` reasoning : ${y.reasoning}`),console.log(` language : ${y.language}`),console.log(` webSearch : ${y.needsWebSearch}`)}return y}parseJSON(e){let t=e.trim();t.startsWith("```")&&(t=t.replace(/^```(?:json)?\s*/,"").replace(/```\s*$/,""));let n=t.match(/\{[\s\S]*\}/);return n&&(t=n[0]),JSON.parse(t)}};var N=require("@langchain/langgraph");var Z=c=>({reducer:(e,t)=>t,default:c}),Hs=N.Annotation.Root({userMessage:(0,N.Annotation)(),doSuggest:(0,N.Annotation)(Z(()=>!0)),overrideLang:(0,N.Annotation)(Z(()=>"")),conversationMessages:(0,N.Annotation)(Z(()=>[])),lastSubagents:(0,N.Annotation)(Z(()=>[])),messageId:(0,N.Annotation)(Z(()=>"")),rewrite:(0,N.Annotation)(Z(()=>null)),effectiveQuery:(0,N.Annotation)(Z(()=>"")),turnLanguage:(0,N.Annotation)(Z(()=>"")),kbContext:(0,N.Annotation)(Z(()=>"")),decision:(0,N.Annotation)(Z(()=>null)),assignment:(0,N.Annotation)(Z(()=>null)),assignmentIndex:(0,N.Annotation)(Z(()=>0)),subResults:(0,N.Annotation)({reducer:(c,e)=>c.concat(e),default:()=>[]}),response:(0,N.Annotation)(Z(()=>null))});function Bo(c){let e=async d=>{c.addUserMessage(d.userMessage),c.needsSummary()&&await c.compactHistory();let m=c.conversationForTurn(),p=c.lastAssistantSubagents(m);return c.debug&&p.length>0&&console.log(`[AgentCore] previous turn handled by: ${p.join(", ")}`),{conversationMessages:m,lastSubagents:p,messageId:c.generateMessageId()}},t=async d=>{let m=await c.rewrite(d.userMessage,d.conversationMessages,d.lastSubagents),p=d.overrideLang||m.language||"";return{rewrite:m,effectiveQuery:m.rewrittenQuery,turnLanguage:p}},n=async d=>{let m=d.rewrite,p=m.isFollowUp?d.effectiveQuery:d.userMessage,h=await c.searchKB(p),f=c.formatKBContext(h),g=await c.tryAnswerFromKB(d.userMessage,h,f,d.messageId);return g?{kbContext:f,response:{...g,messageId:d.messageId,rewrite:m}}:{kbContext:f}},r=async d=>({decision:await c.route(d.effectiveQuery,d.conversationMessages,c.subagentCards(),d.lastSubagents)}),o=async d=>({response:{...await c.answerDirectly(d.effectiveQuery,d.conversationMessages,d.kbContext,d.doSuggest,d.messageId,d.rewrite?.needsWebSearch??!1),messageId:d.messageId,routerDecision:d.decision,rewrite:d.rewrite}}),s=async d=>{let m=await c.runSubagent(d.assignment,d.conversationMessages,d.turnLanguage);return{subResults:[{i:d.assignmentIndex,result:m}]}},a=async d=>{let m=d.subResults.slice().sort((b,T)=>b.i-T.i).map(b=>b.result);c.persistToolMessages(m);let p=c.mergeTrace(m),h=await c.synthesise(d.userMessage,p,c.synthesiserHistory(),d.turnLanguage||void 0),f=m.map(b=>b.subagentName),g=c.collectUiActions(m,d.turnLanguage||null),y=c.collectActionButtons(m,d.turnLanguage||null),k=c.subResultsTriggerNoSuggestions(m);return{response:{...await c.finaliseAnswer({userMessage:d.userMessage,answer:h,subagents:f,uiActions:g,actionButtons:y,messageId:d.messageId},{generateSuggestions:d.doSuggest&&!k&&y.length===0}),messageId:d.messageId,trace:p,routerDecision:d.decision,subagentResults:m,rewrite:d.rewrite,...g.length>0?{uiActions:g}:{},...y.length>0?{actionButtons:y}:{}}}},i=async d=>{let m=(d.rewrite?.mentionedChain||String(c.connectedChain()??"")).trim();return{response:{...await c.answerUnsupportedChain(m,d.turnLanguage,d.messageId),messageId:d.messageId,routerDecision:d.decision,rewrite:d.rewrite}}},l=d=>d.response?N.END:"route",u=d=>{let m=d.decision?.assignments??[];if(m.length===0)return"direct";let p=m.some(f=>f.subagent!=="ai-agent"),h=(d.rewrite?.mentionedChain||c.connectedChain()||"").trim();return p&&h!==""&&!jn(h)?"unsupportedChain":m.map((f,g)=>new N.Send("subagent",{...d,assignment:f,assignmentIndex:g}))};return new N.StateGraph(Hs).addNode("setup",e).addNode("contextualize",t).addNode("kb",n).addNode("route",r).addNode("direct",o).addNode("subagent",s).addNode("synthesize",a).addNode("unsupportedChain",i).addEdge(N.START,"setup").addEdge("setup","contextualize").addEdge("contextualize","kb").addConditionalEdges("kb",l,["route",N.END]).addConditionalEdges("route",u,["direct","subagent","unsupportedChain"]).addEdge("unsupportedChain",N.END).addEdge("direct",N.END).addEdge("subagent","synthesize").addEdge("synthesize",N.END).compile()}var Oo=require("js-sha3");function Pn(){let c=Date.now().toString(16),e=Math.floor(Math.random()*65535).toString(16).padStart(4,"0");return(c+e).slice(-8)}var Gs="You are a helpful AI assistant. Answer the user accurately and concisely. Respond in the same language as the user.",An=class{llm;registry;history;summarizer;router;rewriter;subagents;synthesizer;chatGraph;systemPrompt;debug;secretKey;licenseChecked=!1;userContext={};storage;storageKey;persistHistoryEnabled;kb;kbEntries;vectorKB=null;vectorKBAutoIngested=!1;vectorKBAutoIngestPromise=null;autoIngestEnabled=!1;kbAnswerThreshold;suggestionsEnabled;historyLoaded=!1;historyLoadingPromise=null;historyReady;constructor(e){this.secretKey=e.secretKey,$n(e.rpcUrls),this.llm=new le(e.llm),this.registry=new Ee,this.history=new Nt(e.maxHistoryMessages??50),this.summarizer=new It(this.llm),this.synthesizer=new Mt(this.llm),this.systemPrompt=e.systemPrompt??Gs,this.debug=e.debug??!1,this.suggestionsEnabled=e.generateSuggestions??!0,this.storage=e.storage??null,this.storageKey=e.storageKey??"keyring-agent-history",this.persistHistoryEnabled=e.persistHistory??!1,this.kbEntries=new Dt(e.knowledgeBase),this.kbEntries.setLLM(this.llm);let t=e.vectorKB;t?.enabled?(t.provider?this.kb=t.provider:(this.vectorKB=new Se({url:t.url,token:t.token,namespace:t.namespace,minScore:t.minScore,defaultTopK:t.defaultTopK,debug:this.debug}),this.kb=this.vectorKB,this.autoIngestEnabled=t.autoIngest===!0),this.debug&&console.log(`[AgentCore] vector KB enabled (${t.provider?"custom provider":"Upstash"}`+(t.namespace?`, ns=${t.namespace}`:"")+")")):this.kb=this.kbEntries;let n=e.kbAnswerThreshold;if(typeof n=="number"?this.kbAnswerThreshold=n:t?.enabled&&!t.provider?this.kbAnswerThreshold=t.minScore??1.3:this.kbAnswerThreshold=.8,this.debug&&console.log(`[AgentCore] kbAnswerThreshold = ${this.kbAnswerThreshold}`),e.moralis!==!1){let u=typeof e.moralis=="object"?e.moralis:void 0;this.registry.register(new Fe(u)),this.registry.register(new Le(u)),this.registry.register(new We(u)),this.registry.register(new Ve(u)),this.registry.register(new He(u)),this.registry.register(new Me(u)),this.registry.register(new Ge(u)),this.registry.register(new Ke(u)),this.registry.register(new Xe(u)),this.registry.register(new Ye(u)),this.registry.register(new ze(u)),this.registry.register(new Qe(u)),this.registry.register(new je(u)),this.registry.register(new $e(u)),this.registry.register(new qe(u)),this.registry.register(new Be(u)),this.registry.register(new Oe(u))}let r=e.nftLink;this.registry.register(new Je({url:r})),this.registry.register(new Ze({url:r})),this.registry.register(new et({url:r})),this.registry.register(new tt({url:r})),this.registry.register(new nt(e.llm));let o=e.subagents?.["pool-subgraph"]===!0;if((o?e.subagents?.pool===!0:e.subagents?.pool!==!1)&&e.uniswap!==!1){let u=typeof e.uniswap=="object"?e.uniswap:void 0;this.registry.register(new ot(u)),this.registry.register(new rt(u)),this.registry.register(new st(u)),this.registry.register(new at(u)),this.registry.register(new ft(u));let d=u?{isProduction:u.isProduction}:void 0;this.registry.register(new ht({baseUrl:u?.baseUrl,pool:d,minProvideUsd:u?.minProvideUsd})),this.registry.register(new gt({pool:d}))}if(o&&e.subgraph!==!1){let u=typeof e.subgraph=="object"?e.subgraph:void 0;this.registry.register(new yt(u)),this.registry.register(new bt(u)),this.registry.register(new wt(u)),this.registry.register(new kt(u)),this.registry.register(new Tt(u)),this.registry.register(new vt(u))}let a=e.moralis===!1?void 0:typeof e.moralis=="object"?e.moralis:{};this.registry.register(new Pt(a)),this.registry.register(new At(a)),this.registry.register(new _t(a)),this.registry.register(new Ct(a)),this.registry.register(new Ut(a)),this.registry.register(new Rt(a,{debug:this.debug})),this.registry.register(new Et(a,{debug:this.debug})),this.router=new Lt(this.llm,{debug:this.debug}),this.rewriter=new xn(this.llm,{debug:this.debug});let i=typeof e.subgraph=="object"?e.subgraph:void 0,l=Sn(this.llm,this.registry,{maxToolCalls:e.maxIterations??5,debug:this.debug,linkAddLiquidity:i?.linkAddLiquidity},e.subagents);this.subagents=new Map(l.map(u=>[u.name,u])),this.chatGraph=Bo(this.createGraphHost()),this.historyReady=this.loadHistory()}registerTool(e){this.registry.register(e)}registerTools(e){for(let t of e)this.registry.register(t)}unregisterTool(e){return this.registry.unregister(e)}listTools(){return this.registry.listNames()}async invokeTool(e,t,n){if(this.assertLicense(),await this.loadHistory(),!this.registry.get(e))return{answer:`Unknown tool: "${e}".`,messageId:Pn()};this.debug&&console.log(`[AgentCore] invokeTool ${e} ${JSON.stringify(t)}`);let o=n?.userMessage??`[invoke ${e}]`;this.history.add({role:"user",content:o,timestamp:Date.now()});let s=await this.registry.execute(e,t,this.userContext),a=s.data,i=s.success?a?.summary??"Done.":`Action failed: ${s.error??"unknown error"}`,l=Pn(),u=s.success&&s.ui?[this.stampLanguage(s.ui,null)]:[],d={role:"assistant",content:i,timestamp:Date.now(),subagents:["direct-invoke"],messageId:l};return u.length>0&&(d.uiActions=u),this.history.add(d),await this.persistHistory(),{answer:i,messageId:l,...u.length>0?{uiActions:u}:{}}}registerSubagent(e){this.subagents.set(e.name,e)}unregisterSubagent(e){return this.subagents.delete(e)}listSubagents(){return Array.from(this.subagents.keys())}setKnowledgeBase(e){this.kbEntries.setEntries(e),this.vectorKBAutoIngested=!1}addKnowledgeBase(e){this.kbEntries.addEntries(e),this.vectorKBAutoIngested=!1}async ingestKnowledgeBase(){if(!this.vectorKB)return{count:0};let e=this.kbEntries.getEntries();if(e.length===0)return{count:0};let t=await this.vectorKB.upsert(e);return this.vectorKBAutoIngested=!0,t}async maybeAutoIngest(e){if(!(!e||!this.vectorKB||this.vectorKBAutoIngested)){if(this.vectorKBAutoIngestPromise)return this.vectorKBAutoIngestPromise;this.vectorKBAutoIngestPromise=(async()=>{try{let{count:t}=await this.ingestKnowledgeBase();this.debug&&t>0&&console.log(`[AgentCore] auto-ingested ${t} KB entr${t===1?"y":"ies"} to vector store`)}catch(t){this.debug&&console.warn("[AgentCore] auto-ingest failed:",t)}finally{this.vectorKBAutoIngestPromise=null}})(),await this.vectorKBAutoIngestPromise}}setUserContext(e){this.userContext={...e}}getUserContext(){return{...this.userContext}}async loadHistory(){if(!this.historyLoaded){if(this.historyLoadingPromise)return this.historyLoadingPromise;this.historyLoadingPromise=this._doLoadHistory(),await this.historyLoadingPromise}}async _doLoadHistory(){if(!this.storage){this.debug&&console.log("[AgentCore] loadHistory: no storage configured"),this.historyLoaded=!0;return}if(!this.persistHistoryEnabled){this.debug&&console.log("[AgentCore] loadHistory: persistHistory disabled \u2014 starting fresh session");try{await this.storage.removeItem(this.storageKey)}catch(e){this.debug&&console.warn("[AgentCore] failed to clear stale history:",e)}this.historyLoaded=!0;return}try{let e=await this.storage.getItem(this.storageKey);if(this.debug&&console.log(`[AgentCore] loadHistory: storage key "${this.storageKey}" ${e?`has ${e.length} chars`:"is empty"}`),e){let t=JSON.parse(e);this.history.restore(t),this.debug&&console.log(`[AgentCore] Restored ${this.history.length} messages from storage`)}}catch(e){this.debug&&console.warn("[AgentCore] Failed to load history from storage:",e)}finally{this.historyLoaded=!0}}async persistHistory(){if(this.storage&&this.persistHistoryEnabled)try{let e=JSON.stringify(this.history.serialise());await this.storage.setItem(this.storageKey,e)}catch(e){this.debug&&console.warn("[AgentCore] Failed to persist history to storage:",e)}}assertLicense(){if(this.licenseChecked)return;let e="d00d67291d3c660a5034b01c0b2afbc8d4ac5552099a04f28a92d75fdb44c188";if(e&&(!this.secretKey||(0,Oo.sha3_256)(this.secretKey)!==e))throw new Error("AgentCore: invalid or missing secretKey.");this.licenseChecked=!0}async chat(e,t){this.assertLicense();let n=t?.generateSuggestions??this.suggestionsEnabled,r=typeof t?.language=="string"&&t.language.trim()?t.language.trim():"";await this.loadHistory(),await this.maybeAutoIngest(this.autoIngestEnabled);let o=Date.now();this.debug&&(console.log(`
|
|
821
831
|
${"\u2500".repeat(60)}`),console.log(`[AgentCore] query: "${e}"`));let s=await this.chatGraph.invoke({userMessage:e,doSuggest:n,overrideLang:r});return this.debug&&(console.log(`[AgentCore] turn done in ${Date.now()-o}ms total`),console.log(`${"\u2500".repeat(60)}
|
|
822
832
|
`)),s.response??{answer:"",messageId:Pn()}}createGraphHost(){return{debug:this.debug,addUserMessage:e=>this.addUserMessage(e),needsSummary:()=>this.history.needsSummary(),compactHistory:()=>this.compactHistory(),conversationForTurn:()=>{let e=this.history.getConversation();return e.length>0&&e[e.length-1].role==="user"?e.slice(0,-1):e},lastAssistantSubagents:e=>No(e),generateMessageId:()=>Pn(),rewrite:(e,t,n)=>this.rewriter.rewrite(e,t,n),searchKB:e=>this.searchKB(e),formatKBContext:e=>this.formatKBContext(e),tryAnswerFromKB:(e,t,n,r)=>this.tryAnswerFromKB(e,t,n,r),subagentCards:()=>Array.from(this.subagents.values()).map(e=>e.getCard()),route:(e,t,n,r)=>this.router.route(e,t,n,this.userContext,r),connectedChain:()=>this.userContext.chain,answerUnsupportedChain:(e,t,n)=>this.answerUnsupportedChain(e,t,n),answerDirectly:(e,t,n,r,o,s)=>this.answerDirectly(e,t,n,r,o,s),runSubagent:async(e,t,n)=>{let r=this.subagents.get(e.subagent);if(!r)return this.debug&&console.log(`[AgentCore] WARN: subagent "${e.subagent}" not registered`),{subagentName:e.subagent,steps:[],finalAnswer:`Subagent "${e.subagent}" is not registered.`,toolResults:[],toolMessages:[]};let o=Date.now(),s=await r.run({query:e.query,reasoning:e.reasoning,language:n},t,this.userContext);if(this.debug){let a=s.toolResults.map(i=>i.toolName).join(", ")||"\u2014";console.log(`[AgentCore] ${e.subagent} done in ${Date.now()-o}ms | tools: ${a}`)}return s},persistToolMessages:e=>this.persistToolMessages(e),synthesiserHistory:()=>{let e=this.history.getConversation(),t=e.length>0&&e[e.length-1].role==="user"?e.slice(0,-1):e;return de(t)},mergeTrace:e=>this.mergeTrace(e),synthesise:(e,t,n,r)=>this.synthesizer.synthesise(e,t,n,r),collectUiActions:(e,t)=>this.collectUiActions(e,t),collectActionButtons:(e,t)=>this.collectActionButtons(e,t),subResultsTriggerNoSuggestions:e=>this.subResultsTriggerNoSuggestions(e),finaliseAnswer:(e,t)=>this.finaliseAnswer(e,t)}}subResultsTriggerNoSuggestions(e){for(let t of e)for(let n of t.toolResults){if(!n.success)continue;if(this.registry.get(n.toolName)?.noSuggestions===!0)return!0}return!1}collectActionButtons(e,t){let n=new Map,r=[];for(let o of e)for(let s of o.toolResults)if(!(!s.success||!Array.isArray(s.actionButtons)))for(let a of s.actionButtons){if(!a||typeof a!="object"||typeof a.label!="string"||!a.label.trim()||typeof a.prompt!="string"||!a.prompt.trim())continue;let i={label:a.label,prompt:a.prompt,...t?{language:t}:{}},l=n.get(a.prompt);l?i.label.length>l.label.length&&n.set(a.prompt,i):(n.set(a.prompt,i),r.push(a.prompt))}return r.map(o=>n.get(o))}collectUiActions(e,t){let n=[];for(let r of e)for(let o of r.toolResults)o.success&&o.ui&&typeof o.ui=="object"&&typeof o.ui.component=="string"&&n.push(this.stampLanguage(o.ui,t));if(this.debug&&n.length>0){let r=n.map(o=>`${o.component}=${o.language}`).join(", ");console.log(`[AgentCore] uiActions stamped (rewrite.language="${t??""}"): ${r}`)}return n}stampLanguage(e,t){return!t||e.language?e:{...e,language:t}}addUserMessage(e){this.history.add({role:"user",content:e,timestamp:Date.now()})}async searchKB(e){let t=await this.kb.search(e);if(this.debug)if(t.length===0)console.log(`[AgentCore] KB no hits for "${e}"`);else{let n=t.slice(0,3).map(r=>`${r.score.toFixed(3)} ${this.snippet(r.entry.question,60)}`).join(" | ");console.log(`[AgentCore] KB hits (${t.length}): ${n}`)}return t}snippet(e,t){return e.length<=t?e:e.slice(0,t-1)+"\u2026"}formatKBContext(e){return e.length===0?"":e.map((t,n)=>`[Reference ${n+1}]
|
|
823
833
|
Q: ${t.entry.question}
|
|
@@ -875,4 +885,4 @@ RULES:
|
|
|
875
885
|
- Suggestions must be diverse \u2014 do not repeat the same intent in different words.
|
|
876
886
|
- Use the same language as the user.
|
|
877
887
|
- Good example (pool): ["Add liquidity to USDC/WETH 0.05%", "Estimate yield for $1000 here", "Compare with 0.3% fee tier"]
|
|
878
|
-
- Bad example: ["What is a pool?", "How do pools work?", "Tell me more"] (generic, no action, no specifics)`,timestamp:0}])).text.match(/\[[\s\S]*\]/);return a?JSON.parse(a[0]).filter(l=>typeof l=="string"&&l.length>0).slice(0,3):[]}catch{return[]}}getHistory(){return this.history.getAllFull()}getDisplayHistory(){return this.history.getAllFull().filter(e=>!(e.role!=="user"&&e.role!=="assistant"||e._intermediate||e.role==="assistant"&&!e.content?.trim()))}getLastSuggestions(){let e=this.getDisplayHistory();for(let t=e.length-1;t>=0;t--){let n=e[t];if(n.role==="assistant")return n.suggestedPrompts??[]}return[]}async clearHistory(){this.history.clear(),this.historyLoaded=!0,this.historyLoadingPromise=null,await this.persistHistory()}restoreHistory(e){this.history.restore(e)}serialiseHistory(){return this.history.serialise()}async compactHistory(){this.debug&&console.log("[AgentCore] Summarizing conversation history\u2026");let e=this.history.getAll(),t=6,n=0,r=e.length;for(let m=e.length-1;m>=0;m--)if(e[m].role==="user"&&(n++,n>=t)){r=m;break}let o=e.slice(r),s=Eo(o,o.length),a=e.length-s.length,i=de(e.slice(0,a)),l=this.history.getSummary(),u=[];l&&u.push({role:"user",content:`[Previous context summary]: ${l}`,timestamp:0}),u.push(...i);let d=await this.summarizer.summarize(u);this.history.compactWith(d,s)}persistToolMessages(e){for(let t of e)t.toolMessages.length>0&&(this.history.addMany(t.toolMessages),this.history.add({role:"assistant",content:t.finalAnswer,_intermediate:!0,timestamp:Date.now()}),this.debug&&console.log(`[AgentCore] stored ${t.toolMessages.length} tool message(s) + finalAnswer from ${t.subagentName}`))}getLLMProvider(){return this.llm}getToolRegistry(){return this.registry}};var
|
|
888
|
+
- Bad example: ["What is a pool?", "How do pools work?", "Tell me more"] (generic, no action, no specifics)`,timestamp:0}])).text.match(/\[[\s\S]*\]/);return a?JSON.parse(a[0]).filter(l=>typeof l=="string"&&l.length>0).slice(0,3):[]}catch{return[]}}getHistory(){return this.history.getAllFull()}getDisplayHistory(){return this.history.getAllFull().filter(e=>!(e.role!=="user"&&e.role!=="assistant"||e._intermediate||e.role==="assistant"&&!e.content?.trim()))}getLastSuggestions(){let e=this.getDisplayHistory();for(let t=e.length-1;t>=0;t--){let n=e[t];if(n.role==="assistant")return n.suggestedPrompts??[]}return[]}async clearHistory(){this.history.clear(),this.historyLoaded=!0,this.historyLoadingPromise=null,await this.persistHistory()}restoreHistory(e){this.history.restore(e)}serialiseHistory(){return this.history.serialise()}async compactHistory(){this.debug&&console.log("[AgentCore] Summarizing conversation history\u2026");let e=this.history.getAll(),t=6,n=0,r=e.length;for(let m=e.length-1;m>=0;m--)if(e[m].role==="user"&&(n++,n>=t)){r=m;break}let o=e.slice(r),s=Eo(o,o.length),a=e.length-s.length,i=de(e.slice(0,a)),l=this.history.getSummary(),u=[];l&&u.push({role:"user",content:`[Previous context summary]: ${l}`,timestamp:0}),u.push(...i);let d=await this.summarizer.summarize(u);this.history.compactWith(d,s)}persistToolMessages(e){for(let t of e)t.toolMessages.length>0&&(this.history.addMany(t.toolMessages),this.history.add({role:"assistant",content:t.finalAnswer,_intermediate:!0,timestamp:Date.now()}),this.debug&&console.log(`[AgentCore] stored ${t.toolMessages.length} tool message(s) + finalAnswer from ${t.subagentName}`))}getLLMProvider(){return this.llm}getToolRegistry(){return this.registry}};var Ks=c=>{let e=JSON.parse(c),t=Array.isArray(e)?e:e?.entries;if(!Array.isArray(t))throw new Error("JSON must be an array or { entries: [...] }");return t.map((n,r)=>{if(!n||typeof n.question!="string"||typeof n.answer!="string")throw new Error(`Entry #${r} is missing "question" or "answer"`);return n})};async function $o(c,e){let t=c.entries??(c.json?Ks(c.json):[]);return t.length===0?{count:0}:new Se(e).upsert(t)}0&&(module.exports={AI_AGENT_TOOL_NAMES,AgentCore,ApproveTokenTool,BaseNftMessageTool,BaseSwapService,BaseTool,BaseWalletActionTool,BuyTokenTool,ChatHistory,DEFAULT_PROVIDER,DEFAULT_RPC_BY_CHAIN,DebridgeAdapter,EstimatePoolYieldTool,GeminiProvider,GeminiSearchAiTool,HEX_TO_PANTOGRAPH,KnowledgeBase,MoralisService,NFTContractInfoTool,NFTMetadataTool,NFT_AGENT_TOOL_NAMES,OpenAddLiquidityFormTool,POOL_AGENT_TOOL_NAMES,PantographService,PoolByAddressTool,PoolDetailTool,PoolSearchTool,PoolService,PreviewAddLiquidityTool,RelayAdapter,Router,SWAP_PROVIDER_CONFIG,SendNativeTool,SendNftTool,SendTokenTool,Subagent,SubgraphCoinPoolPairsTool,SubgraphPoolByAddressTool,SubgraphPoolByPositionIdTool,SubgraphPoolSearchTool,SubgraphPositionDetailTool,SubgraphTrendingPoolsTool,Summarizer,SwapServiceFactory,SwapTokenTool,Synthesizer,TOKEN_AGENT_TOOL_NAMES,TRANSFER_TOPIC,TRENDING_DEFAULT_BY_HEX_CHAIN,TokenAnalyticsTool,TokenHoldersTool,TokenInfoTool,TokenScoreTool,ToolRegistry,TopGainersTool,TopPoolsTool,TransactionByHashTool,TrendingTokensTool,UNISWAP_CHAIN_SLUG,UnwrapNativeTool,UpstashKnowledgeBase,WALLET_ACTION_AGENT_TOOL_NAMES,WALLET_AGENT_TOOL_NAMES,WalletApprovalsTool,WalletDefiPositionsTool,WalletDefiProtocolPositionsTool,WalletDefiSummaryTool,WalletHistoryTool,WalletNFTsTool,WalletNetWorthTool,WalletNftTransfersTool,WalletPnlSummaryTool,WalletPnlTool,WalletTokenBalancesTool,WalletTokenTransfersTool,WrapNativeTool,ZERO_ADDRESS,buildRangePresets,buildUniswapPoolUrl,clampTick,createAiAgent,createDefaultSubagents,createNftAgent,createPoolAgent,createTokenAgent,createWalletActionAgent,createWalletAgent,ethCallAt,ethCallByChain,getAffiliateFee,getChainMeta,getDefaultRpcUrl,getGatewayAddress,getNativeTokenInfo,getPositionManagerAddress,getProviderByChain,ingestKnowledgeBase,priceToTick,resolveRpcUrl,roundTickToSpacing,rpcCall,setRpcOverrides,swapServiceFactory,tickToPrice,toPantographChain});
|
package/package.json
CHANGED