@t2000/engine 0.46.16 → 0.47.0
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/README.md +59 -24
- package/dist/index.d.ts +121 -71
- package/dist/index.js +432 -500
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
-
import { ALL_NAVI_ASSETS,
|
|
2
|
+
import { ALL_NAVI_ASSETS, getDecimalsForCoinType, resolveSymbol, assertAllowedAsset, SUPPORTED_ASSETS, getSwapQuote, extractTransferDetails, classifyTransaction } from '@t2000/sdk';
|
|
3
|
+
import { randomUUID } from 'crypto';
|
|
3
4
|
import { readdirSync, readFileSync } from 'fs';
|
|
4
5
|
import { join } from 'path';
|
|
5
6
|
import yaml from 'js-yaml';
|
|
@@ -225,44 +226,6 @@ function applyToolFlags(tools) {
|
|
|
225
226
|
function getToolFlags(name) {
|
|
226
227
|
return TOOL_FLAGS[name] ?? {};
|
|
227
228
|
}
|
|
228
|
-
var SUI_MAINNET_URL = "https://fullnode.mainnet.sui.io:443";
|
|
229
|
-
var EXTRA_COINS = {
|
|
230
|
-
"0xc060006111016b8a020ad5b33834984a437aaa7d3c74c18e09a95d48aceab08c::coin::COIN": { symbol: "USDT", decimals: 6 }
|
|
231
|
-
};
|
|
232
|
-
async function fetchWalletCoins(address, rpcUrl) {
|
|
233
|
-
const url = rpcUrl || SUI_MAINNET_URL;
|
|
234
|
-
const res = await fetch(url, {
|
|
235
|
-
method: "POST",
|
|
236
|
-
headers: { "Content-Type": "application/json" },
|
|
237
|
-
body: JSON.stringify({
|
|
238
|
-
jsonrpc: "2.0",
|
|
239
|
-
id: 1,
|
|
240
|
-
method: "suix_getAllBalances",
|
|
241
|
-
params: [address]
|
|
242
|
-
}),
|
|
243
|
-
signal: AbortSignal.timeout(8e3)
|
|
244
|
-
});
|
|
245
|
-
if (!res.ok) {
|
|
246
|
-
throw new Error(`Sui RPC error: ${res.status} ${res.statusText}`);
|
|
247
|
-
}
|
|
248
|
-
const json = await res.json();
|
|
249
|
-
if (json.error) {
|
|
250
|
-
throw new Error(`Sui RPC error: ${json.error.message}`);
|
|
251
|
-
}
|
|
252
|
-
const balances = json.result ?? [];
|
|
253
|
-
return balances.map((b) => {
|
|
254
|
-
const extra = EXTRA_COINS[b.coinType];
|
|
255
|
-
const symbol = extra?.symbol ?? resolveSymbol(b.coinType);
|
|
256
|
-
const decimals = extra?.decimals ?? getDecimalsForCoinType(b.coinType);
|
|
257
|
-
return {
|
|
258
|
-
coinType: b.coinType,
|
|
259
|
-
symbol,
|
|
260
|
-
decimals,
|
|
261
|
-
totalBalance: b.totalBalance,
|
|
262
|
-
coinObjectCount: b.coinObjectCount
|
|
263
|
-
};
|
|
264
|
-
});
|
|
265
|
-
}
|
|
266
229
|
|
|
267
230
|
// src/navi-config.ts
|
|
268
231
|
var NAVI_SERVER_NAME = "navi";
|
|
@@ -494,59 +457,265 @@ function parseMcpJson(content) {
|
|
|
494
457
|
return text;
|
|
495
458
|
}
|
|
496
459
|
}
|
|
460
|
+
var SUI_MAINNET_URL = "https://fullnode.mainnet.sui.io:443";
|
|
461
|
+
var EXTRA_COINS = {
|
|
462
|
+
"0xc060006111016b8a020ad5b33834984a437aaa7d3c74c18e09a95d48aceab08c::coin::COIN": { symbol: "USDT", decimals: 6 }
|
|
463
|
+
};
|
|
464
|
+
async function fetchWalletCoins(address, rpcUrl) {
|
|
465
|
+
const url = rpcUrl || SUI_MAINNET_URL;
|
|
466
|
+
const res = await fetch(url, {
|
|
467
|
+
method: "POST",
|
|
468
|
+
headers: { "Content-Type": "application/json" },
|
|
469
|
+
body: JSON.stringify({
|
|
470
|
+
jsonrpc: "2.0",
|
|
471
|
+
id: 1,
|
|
472
|
+
method: "suix_getAllBalances",
|
|
473
|
+
params: [address]
|
|
474
|
+
}),
|
|
475
|
+
signal: AbortSignal.timeout(8e3)
|
|
476
|
+
});
|
|
477
|
+
if (!res.ok) {
|
|
478
|
+
throw new Error(`Sui RPC error: ${res.status} ${res.statusText}`);
|
|
479
|
+
}
|
|
480
|
+
const json = await res.json();
|
|
481
|
+
if (json.error) {
|
|
482
|
+
throw new Error(`Sui RPC error: ${json.error.message}`);
|
|
483
|
+
}
|
|
484
|
+
const balances = json.result ?? [];
|
|
485
|
+
return balances.map((b) => {
|
|
486
|
+
const extra = EXTRA_COINS[b.coinType];
|
|
487
|
+
const symbol = extra?.symbol ?? resolveSymbol(b.coinType);
|
|
488
|
+
const decimals = extra?.decimals ?? getDecimalsForCoinType(b.coinType);
|
|
489
|
+
return {
|
|
490
|
+
coinType: b.coinType,
|
|
491
|
+
symbol,
|
|
492
|
+
decimals,
|
|
493
|
+
totalBalance: b.totalBalance,
|
|
494
|
+
coinObjectCount: b.coinObjectCount
|
|
495
|
+
};
|
|
496
|
+
});
|
|
497
|
+
}
|
|
497
498
|
|
|
498
|
-
// src/
|
|
499
|
-
var
|
|
500
|
-
var
|
|
501
|
-
var
|
|
502
|
-
var
|
|
503
|
-
|
|
504
|
-
|
|
499
|
+
// src/blockvision-prices.ts
|
|
500
|
+
var BLOCKVISION_BASE = "https://api.blockvision.org/v2/sui";
|
|
501
|
+
var PORTFOLIO_TIMEOUT_MS = 4e3;
|
|
502
|
+
var PRICES_TIMEOUT_MS = 3e3;
|
|
503
|
+
var CACHE_TTL_MS = 6e4;
|
|
504
|
+
var PRICE_LIST_CHUNK = 10;
|
|
505
|
+
var STABLE_USD_PRICES = {
|
|
506
|
+
"0xdba34672e30cb065b1f93e3ab55318768fd6fef66c15942c9f7cb846e2f900e7::usdc::USDC": 1,
|
|
507
|
+
"0x375f70cf2ae4c00bf37117d0c85a2c71545e6ee05c4a5c7d282cd66a4504b068::usdt::USDT": 1,
|
|
508
|
+
"0x41d587e5336f1c86cad50d38a7136db99333bb9bda91cea4ba69115defeb1402::sui_usde::SUI_USDE": 1,
|
|
509
|
+
"0x44f838219cf67b058f3b37907b655f226153c18e33dfcd0da559a844fea9b1c1::usdsui::USDSUI": 1,
|
|
510
|
+
"0x5d4b302506645c37ff133b98c4b50a5ae14841659738d6d733d59d0d217a93bf::coin::COIN": 1,
|
|
511
|
+
"0xc060006111016b8a020ad5b33834984a437aaa7d3c74c18e09a95d48aceab08c::coin::COIN": 1
|
|
512
|
+
};
|
|
513
|
+
var portfolioCache = /* @__PURE__ */ new Map();
|
|
514
|
+
var portfolioInflight = /* @__PURE__ */ new Map();
|
|
515
|
+
var priceMapCache = null;
|
|
516
|
+
async function fetchAddressPortfolio(address, apiKey, fallbackRpcUrl) {
|
|
505
517
|
const now = Date.now();
|
|
506
|
-
const
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
518
|
+
const cached = portfolioCache.get(address);
|
|
519
|
+
if (cached && now - cached.ts < CACHE_TTL_MS) {
|
|
520
|
+
return cached.data;
|
|
521
|
+
}
|
|
522
|
+
let inflight = portfolioInflight.get(address);
|
|
523
|
+
if (inflight) return inflight;
|
|
524
|
+
inflight = (async () => {
|
|
525
|
+
try {
|
|
526
|
+
if (apiKey && apiKey.trim().length > 0) {
|
|
527
|
+
const blockvision = await fetchPortfolioFromBlockVision(address, apiKey);
|
|
528
|
+
if (blockvision) {
|
|
529
|
+
portfolioCache.set(address, { data: blockvision, ts: Date.now() });
|
|
530
|
+
return blockvision;
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
const degraded = await fetchPortfolioFromSuiRpc(address, fallbackRpcUrl);
|
|
534
|
+
portfolioCache.set(address, { data: degraded, ts: Date.now() });
|
|
535
|
+
return degraded;
|
|
536
|
+
} finally {
|
|
537
|
+
portfolioInflight.delete(address);
|
|
538
|
+
}
|
|
539
|
+
})();
|
|
540
|
+
portfolioInflight.set(address, inflight);
|
|
541
|
+
return inflight;
|
|
542
|
+
}
|
|
543
|
+
async function fetchPortfolioFromBlockVision(address, apiKey) {
|
|
544
|
+
const url = `${BLOCKVISION_BASE}/account/coins?account=${encodeURIComponent(address)}`;
|
|
545
|
+
let res;
|
|
546
|
+
try {
|
|
547
|
+
res = await fetch(url, {
|
|
548
|
+
headers: { "x-api-key": apiKey, accept: "application/json" },
|
|
549
|
+
signal: AbortSignal.timeout(PORTFOLIO_TIMEOUT_MS)
|
|
515
550
|
});
|
|
516
|
-
|
|
551
|
+
} catch (err) {
|
|
552
|
+
console.warn("[blockvision-prices] portfolio fetch threw, degrading:", err);
|
|
553
|
+
return null;
|
|
517
554
|
}
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
555
|
+
if (!res.ok) {
|
|
556
|
+
console.warn(`[blockvision-prices] portfolio HTTP ${res.status}, degrading`);
|
|
557
|
+
return null;
|
|
558
|
+
}
|
|
559
|
+
let json;
|
|
560
|
+
try {
|
|
561
|
+
json = await res.json();
|
|
562
|
+
} catch (err) {
|
|
563
|
+
console.warn("[blockvision-prices] portfolio JSON parse failed, degrading:", err);
|
|
564
|
+
return null;
|
|
565
|
+
}
|
|
566
|
+
if (json.code !== 200 || !json.result) {
|
|
567
|
+
console.warn(`[blockvision-prices] portfolio code=${json.code} msg=${json.message}, degrading`);
|
|
568
|
+
return null;
|
|
569
|
+
}
|
|
570
|
+
const rawCoins = json.result.coins ?? [];
|
|
571
|
+
const coins = rawCoins.map((c) => {
|
|
572
|
+
const coinType = c.coinType;
|
|
573
|
+
const symbol = c.symbol || resolveSymbol(coinType);
|
|
574
|
+
const decimals = typeof c.decimals === "number" ? c.decimals : getDecimalsForCoinType(coinType);
|
|
575
|
+
const stablePrice = STABLE_USD_PRICES[coinType];
|
|
576
|
+
const apiPrice = parseNumberOrNull(c.price);
|
|
577
|
+
const apiUsd = parseNumberOrNull(c.usdValue);
|
|
578
|
+
const price = apiPrice ?? stablePrice ?? null;
|
|
579
|
+
let usdValue = apiUsd;
|
|
580
|
+
if (usdValue == null && price != null) {
|
|
581
|
+
const amount = Number(c.balance) / 10 ** decimals;
|
|
582
|
+
usdValue = Number.isFinite(amount) ? amount * price : null;
|
|
583
|
+
}
|
|
584
|
+
return {
|
|
585
|
+
coinType,
|
|
586
|
+
symbol,
|
|
587
|
+
decimals,
|
|
588
|
+
balance: c.balance,
|
|
589
|
+
price,
|
|
590
|
+
usdValue
|
|
591
|
+
};
|
|
592
|
+
});
|
|
593
|
+
const apiTotal = parseNumberOrNull(json.result.usdValue);
|
|
594
|
+
const totalUsd = apiTotal ?? coins.reduce((sum, c) => sum + (c.usdValue ?? 0), 0);
|
|
595
|
+
return {
|
|
596
|
+
coins,
|
|
597
|
+
totalUsd,
|
|
598
|
+
pricedAt: Date.now(),
|
|
599
|
+
source: "blockvision"
|
|
600
|
+
};
|
|
522
601
|
}
|
|
523
|
-
async function
|
|
524
|
-
const
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
signal: AbortSignal.timeout(1e4)
|
|
602
|
+
async function fetchPortfolioFromSuiRpc(address, fallbackRpcUrl) {
|
|
603
|
+
const walletCoins = await fetchWalletCoins(address, fallbackRpcUrl).catch((err) => {
|
|
604
|
+
console.warn("[blockvision-prices] sui rpc coin fetch failed:", err);
|
|
605
|
+
return [];
|
|
528
606
|
});
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
607
|
+
const coins = walletCoins.map((c) => {
|
|
608
|
+
const stablePrice = STABLE_USD_PRICES[c.coinType] ?? null;
|
|
609
|
+
const amount = Number(c.totalBalance) / 10 ** c.decimals;
|
|
610
|
+
const usdValue = stablePrice != null && Number.isFinite(amount) ? amount * stablePrice : null;
|
|
611
|
+
return {
|
|
612
|
+
coinType: c.coinType,
|
|
613
|
+
symbol: c.symbol,
|
|
614
|
+
decimals: c.decimals,
|
|
615
|
+
balance: c.totalBalance,
|
|
616
|
+
price: stablePrice,
|
|
617
|
+
usdValue
|
|
618
|
+
};
|
|
619
|
+
});
|
|
620
|
+
const totalUsd = coins.reduce((sum, c) => sum + (c.usdValue ?? 0), 0);
|
|
621
|
+
return {
|
|
622
|
+
coins,
|
|
623
|
+
totalUsd,
|
|
624
|
+
pricedAt: Date.now(),
|
|
625
|
+
source: "sui-rpc-degraded"
|
|
626
|
+
};
|
|
627
|
+
}
|
|
628
|
+
async function fetchTokenPrices(coinTypes, apiKey) {
|
|
629
|
+
if (coinTypes.length === 0) return {};
|
|
630
|
+
const now = Date.now();
|
|
631
|
+
const cacheValid = priceMapCache !== null && now - priceMapCache.ts < CACHE_TTL_MS;
|
|
632
|
+
const cached = cacheValid ? priceMapCache.prices : {};
|
|
633
|
+
const result = {};
|
|
634
|
+
const stillMissing = [];
|
|
635
|
+
for (const coinType of coinTypes) {
|
|
636
|
+
if (cached[coinType]) {
|
|
637
|
+
result[coinType] = cached[coinType];
|
|
638
|
+
continue;
|
|
639
|
+
}
|
|
640
|
+
const stable = STABLE_USD_PRICES[coinType];
|
|
641
|
+
if (typeof stable === "number") {
|
|
642
|
+
result[coinType] = { price: stable };
|
|
643
|
+
continue;
|
|
644
|
+
}
|
|
645
|
+
stillMissing.push(coinType);
|
|
532
646
|
}
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
647
|
+
if (stillMissing.length === 0) return result;
|
|
648
|
+
if (!apiKey || apiKey.trim().length === 0) {
|
|
649
|
+
return result;
|
|
650
|
+
}
|
|
651
|
+
const fetched = await fetchPricesFromBlockVision(stillMissing, apiKey);
|
|
652
|
+
Object.assign(result, fetched);
|
|
653
|
+
const merged = { ...cached, ...fetched };
|
|
654
|
+
priceMapCache = { prices: merged, ts: cacheValid ? priceMapCache.ts : now };
|
|
655
|
+
return result;
|
|
656
|
+
}
|
|
657
|
+
async function fetchPricesFromBlockVision(coinTypes, apiKey) {
|
|
658
|
+
const out = {};
|
|
659
|
+
for (let i = 0; i < coinTypes.length; i += PRICE_LIST_CHUNK) {
|
|
660
|
+
const chunk = coinTypes.slice(i, i + PRICE_LIST_CHUNK);
|
|
661
|
+
const tokenIds = encodeURIComponent(chunk.join(","));
|
|
662
|
+
const url = `${BLOCKVISION_BASE}/coin/price/list?tokenIds=${tokenIds}&show24hChange=true`;
|
|
663
|
+
let res;
|
|
664
|
+
try {
|
|
665
|
+
res = await fetch(url, {
|
|
666
|
+
headers: { "x-api-key": apiKey, accept: "application/json" },
|
|
667
|
+
signal: AbortSignal.timeout(PRICES_TIMEOUT_MS)
|
|
668
|
+
});
|
|
669
|
+
} catch (err) {
|
|
670
|
+
console.warn("[blockvision-prices] price chunk threw, skipping:", err);
|
|
671
|
+
continue;
|
|
672
|
+
}
|
|
673
|
+
if (!res.ok) {
|
|
674
|
+
console.warn(`[blockvision-prices] price chunk HTTP ${res.status}`);
|
|
675
|
+
continue;
|
|
676
|
+
}
|
|
677
|
+
let json;
|
|
678
|
+
try {
|
|
679
|
+
json = await res.json();
|
|
680
|
+
} catch (err) {
|
|
681
|
+
console.warn("[blockvision-prices] price chunk JSON parse failed:", err);
|
|
682
|
+
continue;
|
|
683
|
+
}
|
|
684
|
+
if (json.code !== 200 || !json.result) continue;
|
|
685
|
+
const prices = json.result.prices ?? {};
|
|
686
|
+
const changes = json.result.coin24HChange ?? {};
|
|
687
|
+
for (const [coinType, priceStr] of Object.entries(prices)) {
|
|
688
|
+
const price = parseNumberOrNull(priceStr);
|
|
689
|
+
if (price == null) continue;
|
|
690
|
+
const change24h = parseNumberOrNull(changes[coinType]);
|
|
691
|
+
out[coinType] = change24h == null ? { price } : { price, change24h };
|
|
539
692
|
}
|
|
540
693
|
}
|
|
541
|
-
|
|
542
|
-
|
|
694
|
+
return out;
|
|
695
|
+
}
|
|
696
|
+
function parseNumberOrNull(input) {
|
|
697
|
+
if (typeof input === "number") return Number.isFinite(input) ? input : null;
|
|
698
|
+
if (typeof input !== "string" || input.trim().length === 0) return null;
|
|
699
|
+
const n = Number(input);
|
|
700
|
+
return Number.isFinite(n) ? n : null;
|
|
701
|
+
}
|
|
702
|
+
function clearPortfolioCache() {
|
|
703
|
+
portfolioCache.clear();
|
|
704
|
+
portfolioInflight.clear();
|
|
543
705
|
}
|
|
544
|
-
function
|
|
545
|
-
|
|
706
|
+
function clearPortfolioCacheFor(address) {
|
|
707
|
+
portfolioCache.delete(address);
|
|
708
|
+
portfolioInflight.delete(address);
|
|
709
|
+
}
|
|
710
|
+
function clearPriceMapCache() {
|
|
711
|
+
priceMapCache = null;
|
|
546
712
|
}
|
|
547
713
|
|
|
548
714
|
// src/tools/balance.ts
|
|
549
715
|
var GAS_RESERVE_SUI2 = 0.05;
|
|
716
|
+
var VSUI_COIN_TYPE = "0x549e8b69270defbfafd4f94e17ec44cdbdd99820b33bda2278dea3b9a32d3f55::cert::CERT";
|
|
717
|
+
var SUI_COIN_TYPE = "0x2::sui::SUI";
|
|
718
|
+
var VSUI_FALLBACK_RATE = 1.05;
|
|
550
719
|
async function callNavi(manager, tool, args = {}) {
|
|
551
720
|
const result = await manager.callTool(NAVI_SERVER_NAME, tool, args);
|
|
552
721
|
if (result.isError) {
|
|
@@ -555,83 +724,103 @@ async function callNavi(manager, tool, args = {}) {
|
|
|
555
724
|
}
|
|
556
725
|
return parseMcpJson(result.content);
|
|
557
726
|
}
|
|
727
|
+
async function applyVsuiPriceFallback(portfolio) {
|
|
728
|
+
const vsuiIdx = portfolio.coins.findIndex((c) => c.coinType === VSUI_COIN_TYPE);
|
|
729
|
+
if (vsuiIdx === -1) return;
|
|
730
|
+
const vsui = portfolio.coins[vsuiIdx];
|
|
731
|
+
if (vsui.price != null) return;
|
|
732
|
+
const suiCoin = portfolio.coins.find((c) => c.coinType === SUI_COIN_TYPE);
|
|
733
|
+
const suiPrice = suiCoin?.price ?? null;
|
|
734
|
+
if (suiPrice == null) return;
|
|
735
|
+
let rate = VSUI_FALLBACK_RATE;
|
|
736
|
+
try {
|
|
737
|
+
const statsRes = await fetch("https://open-api.naviprotocol.io/api/volo/stats", {
|
|
738
|
+
signal: AbortSignal.timeout(5e3)
|
|
739
|
+
});
|
|
740
|
+
if (statsRes.ok) {
|
|
741
|
+
const json = await statsRes.json();
|
|
742
|
+
const data = json.data ?? json;
|
|
743
|
+
rate = data.exchange_rate ?? data.exchangeRate ?? VSUI_FALLBACK_RATE;
|
|
744
|
+
}
|
|
745
|
+
} catch {
|
|
746
|
+
}
|
|
747
|
+
const price = rate * suiPrice;
|
|
748
|
+
const amount = Number(vsui.balance) / 10 ** vsui.decimals;
|
|
749
|
+
const usdValue = Number.isFinite(amount) ? amount * price : null;
|
|
750
|
+
const previousUsd = vsui.usdValue ?? 0;
|
|
751
|
+
portfolio.coins[vsuiIdx] = { ...vsui, price, usdValue };
|
|
752
|
+
if (usdValue != null) {
|
|
753
|
+
portfolio.totalUsd = portfolio.totalUsd - previousUsd + usdValue;
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
async function loadPortfolio(address, blockvisionApiKey, fallbackRpcUrl, cache) {
|
|
757
|
+
if (cache) {
|
|
758
|
+
const hit = cache.get(address);
|
|
759
|
+
if (hit) return hit;
|
|
760
|
+
}
|
|
761
|
+
const portfolio = await fetchAddressPortfolio(address, blockvisionApiKey, fallbackRpcUrl);
|
|
762
|
+
if (cache) cache.set(address, portfolio);
|
|
763
|
+
return portfolio;
|
|
764
|
+
}
|
|
558
765
|
var balanceCheckTool = buildTool({
|
|
559
766
|
name: "balance_check",
|
|
560
767
|
description: "Get the user's full balance breakdown. Returns wallet holdings (tokens the user owns \u2014 NOT savings), NAVI savings deposits (USDC deposited into NAVI Protocol earning yield), outstanding debt, pending rewards, gas reserve, total net worth, and saveableUsdc (only USDC can be deposited into savings). IMPORTANT: wallet holdings like GOLD, SUI, USDT are NOT savings positions \u2014 they are just tokens sitting in the wallet.",
|
|
561
768
|
inputSchema: z.object({}),
|
|
562
769
|
jsonSchema: { type: "object", properties: {}, required: [] },
|
|
563
770
|
isReadOnly: true,
|
|
564
|
-
// [v1.
|
|
565
|
-
//
|
|
566
|
-
//
|
|
771
|
+
// [v1.4 BlockVision] Wallet contents change after every send / swap /
|
|
772
|
+
// save / etc. and the price half of this result is sourced from
|
|
773
|
+
// BlockVision's Indexer REST API. Microcompact must NEVER dedupe these
|
|
774
|
+
// calls — each one reflects a different on-chain + market snapshot.
|
|
567
775
|
cacheable: false,
|
|
568
776
|
async call(_input, context) {
|
|
569
777
|
if (hasNaviMcp(context)) {
|
|
570
778
|
const address = getWalletAddress(context);
|
|
571
779
|
const mgr = getMcpManager(context);
|
|
572
780
|
const hasPositionFetcher = !!(context.positionFetcher && context.walletAddress);
|
|
573
|
-
const [
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
781
|
+
const [portfolio, positions, rewards, serverPositions] = await Promise.all([
|
|
782
|
+
loadPortfolio(
|
|
783
|
+
address,
|
|
784
|
+
context.blockvisionApiKey,
|
|
785
|
+
context.suiRpcUrl,
|
|
786
|
+
context.portfolioCache
|
|
787
|
+
).catch((err) => {
|
|
788
|
+
console.warn("[balance_check] portfolio fetch failed, returning empty:", err);
|
|
789
|
+
const fallback = {
|
|
790
|
+
coins: [],
|
|
791
|
+
totalUsd: 0,
|
|
792
|
+
pricedAt: Date.now(),
|
|
793
|
+
source: "sui-rpc-degraded"
|
|
794
|
+
};
|
|
795
|
+
return fallback;
|
|
577
796
|
}),
|
|
578
797
|
hasPositionFetcher ? Promise.resolve(null) : callNavi(mgr, NaviTools.GET_POSITIONS, {
|
|
579
798
|
address,
|
|
580
799
|
protocols: "navi",
|
|
581
800
|
format: "json"
|
|
801
|
+
}).catch((err) => {
|
|
802
|
+
console.warn("[balance_check] NAVI GET_POSITIONS failed:", err);
|
|
803
|
+
return null;
|
|
582
804
|
}),
|
|
583
|
-
hasPositionFetcher ? Promise.resolve(null) : callNavi(mgr, NaviTools.GET_AVAILABLE_REWARDS, { address })
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
if (!coins || coins.length === 0) {
|
|
587
|
-
const mcpCoins = await callNavi(mgr, NaviTools.GET_COINS, { address }).catch(() => []);
|
|
588
|
-
const coinArr = Array.isArray(mcpCoins) ? mcpCoins : [];
|
|
589
|
-
coins = coinArr.map((c) => ({
|
|
590
|
-
coinType: c.coinType ?? "",
|
|
591
|
-
symbol: c.symbol ?? "",
|
|
592
|
-
decimals: c.decimals ?? getDecimalsForCoinType(c.coinType ?? ""),
|
|
593
|
-
totalBalance: c.totalBalance ?? "0",
|
|
594
|
-
coinObjectCount: 0
|
|
595
|
-
}));
|
|
596
|
-
}
|
|
597
|
-
const VSUI_COIN_TYPE = "0x549e8b69270defbfafd4f94e17ec44cdbdd99820b33bda2278dea3b9a32d3f55::cert::CERT";
|
|
598
|
-
const coinTypes = coins.map((c) => c.coinType).filter(Boolean);
|
|
599
|
-
const [prices, serverPositions] = await Promise.all([
|
|
600
|
-
fetchTokenPrices(coinTypes).catch((err) => {
|
|
601
|
-
console.warn("[balance_check] DefiLlama price fetch failed:", err);
|
|
602
|
-
return {};
|
|
805
|
+
hasPositionFetcher ? Promise.resolve(null) : callNavi(mgr, NaviTools.GET_AVAILABLE_REWARDS, { address }).catch((err) => {
|
|
806
|
+
console.warn("[balance_check] NAVI GET_AVAILABLE_REWARDS failed:", err);
|
|
807
|
+
return null;
|
|
603
808
|
}),
|
|
604
809
|
hasPositionFetcher ? context.positionFetcher(context.walletAddress).catch((err) => {
|
|
605
810
|
console.warn("[balance_check] positionFetcher failed:", err);
|
|
606
811
|
return null;
|
|
607
812
|
}) : Promise.resolve(null)
|
|
608
813
|
]);
|
|
609
|
-
|
|
610
|
-
try {
|
|
611
|
-
const statsRes = await fetch("https://open-api.naviprotocol.io/api/volo/stats", {
|
|
612
|
-
signal: AbortSignal.timeout(5e3)
|
|
613
|
-
});
|
|
614
|
-
if (statsRes.ok) {
|
|
615
|
-
const statsJson = await statsRes.json();
|
|
616
|
-
const d = statsJson.data ?? statsJson;
|
|
617
|
-
const rate = d.exchange_rate ?? d.exchangeRate ?? 1.05;
|
|
618
|
-
const suiPrice = prices["0x2::sui::SUI"] ?? 0;
|
|
619
|
-
prices[VSUI_COIN_TYPE] = rate * suiPrice;
|
|
620
|
-
}
|
|
621
|
-
} catch {
|
|
622
|
-
const suiPrice = prices["0x2::sui::SUI"] ?? 0;
|
|
623
|
-
prices[VSUI_COIN_TYPE] = suiPrice * 1.05;
|
|
624
|
-
}
|
|
625
|
-
}
|
|
814
|
+
await applyVsuiPriceFallback(portfolio);
|
|
626
815
|
let availableUsd = 0;
|
|
627
816
|
let stablesUsd = 0;
|
|
628
817
|
let gasReserveUsd2 = 0;
|
|
629
818
|
const STABLE_SYMBOLS = /* @__PURE__ */ new Set(["USDC", "USDT", "USDe", "USDsui", "wUSDC", "wUSDT"]);
|
|
630
819
|
const holdings = [];
|
|
631
|
-
for (const coin of coins) {
|
|
632
|
-
const balance2 = Number(coin.
|
|
633
|
-
const price =
|
|
634
|
-
if (coin.symbol === "SUI" || coin.coinType ===
|
|
820
|
+
for (const coin of portfolio.coins) {
|
|
821
|
+
const balance2 = Number(coin.balance) / 10 ** coin.decimals;
|
|
822
|
+
const price = coin.price ?? 0;
|
|
823
|
+
if (coin.symbol === "SUI" || coin.coinType === SUI_COIN_TYPE) {
|
|
635
824
|
const reserveAmount = Math.min(balance2, GAS_RESERVE_SUI2);
|
|
636
825
|
gasReserveUsd2 = reserveAmount * price;
|
|
637
826
|
availableUsd += (balance2 - reserveAmount) * price;
|
|
@@ -676,7 +865,8 @@ var balanceCheckTool = buildTool({
|
|
|
676
865
|
total: availableUsd + savings + gasReserveUsd2 + pendingRewardsUsd - debt,
|
|
677
866
|
stables: stablesUsd,
|
|
678
867
|
holdings: visibleHoldings,
|
|
679
|
-
saveableUsdc
|
|
868
|
+
saveableUsdc,
|
|
869
|
+
priceSource: portfolio.source
|
|
680
870
|
};
|
|
681
871
|
const holdingsList = visibleHoldings.map((h) => `${h.symbol}: ${h.balance < 1 ? h.balance.toFixed(6) : h.balance.toFixed(2)} ($${h.usdValue.toFixed(2)})`).join(", ");
|
|
682
872
|
return {
|
|
@@ -993,7 +1183,6 @@ var healthCheckTool = buildTool({
|
|
|
993
1183
|
};
|
|
994
1184
|
}
|
|
995
1185
|
});
|
|
996
|
-
var YIELDS_API = "https://yields.llama.fi";
|
|
997
1186
|
var STABLECOIN_SYMBOLS2 = /* @__PURE__ */ new Set([
|
|
998
1187
|
"usdc",
|
|
999
1188
|
"wusdc",
|
|
@@ -1027,24 +1216,9 @@ function applyFilters(rates, opts) {
|
|
|
1027
1216
|
function formatRatesSummary(rates) {
|
|
1028
1217
|
return Object.entries(rates).map(([asset, r]) => `${asset}: Save ${(r.saveApy * 100).toFixed(2)}% / Borrow ${(r.borrowApy * 100).toFixed(2)}%`).join(", ");
|
|
1029
1218
|
}
|
|
1030
|
-
async function fetchRatesFromDefiLlama() {
|
|
1031
|
-
const res = await fetch(`${YIELDS_API}/pools`, { signal: AbortSignal.timeout(15e3) });
|
|
1032
|
-
if (!res.ok) throw new Error(`DefiLlama API error: HTTP ${res.status}`);
|
|
1033
|
-
const data = await res.json();
|
|
1034
|
-
const naviPools = (data.data ?? []).filter(
|
|
1035
|
-
(p) => p.chain === "Sui" && p.project === "navi-lending" && p.tvlUsd > 1e4
|
|
1036
|
-
);
|
|
1037
|
-
const result = {};
|
|
1038
|
-
for (const pool of naviPools) {
|
|
1039
|
-
const saveApy = (pool.apy ?? 0) / 100;
|
|
1040
|
-
const borrowApy = pool.apyBorrow != null ? Math.abs(pool.apyBorrow) / 100 : 0;
|
|
1041
|
-
result[pool.symbol] = { saveApy, borrowApy };
|
|
1042
|
-
}
|
|
1043
|
-
return result;
|
|
1044
|
-
}
|
|
1045
1219
|
var ratesInfoTool = buildTool({
|
|
1046
1220
|
name: "rates_info",
|
|
1047
|
-
description: 'NAVI Protocol lending markets ONLY (single-sided save/borrow, no impermanent-loss risk). Use this for stablecoin and bluechip lending yields. Renders a rich rates card. Filter args: `assets` (specific symbols like ["USDC"]), `stableOnly` (true to show only USD-pegged assets), `topN` (max rows in card, default 8, max 50).
|
|
1221
|
+
description: 'NAVI Protocol lending markets ONLY (single-sided save/borrow, no impermanent-loss risk). Use this for stablecoin and bluechip lending yields. Renders a rich rates card. Filter args: `assets` (specific symbols like ["USDC"]), `stableOnly` (true to show only USD-pegged assets), `topN` (max rows in card, default 8, max 50).',
|
|
1048
1222
|
inputSchema: z.object({
|
|
1049
1223
|
assets: z.array(z.string()).optional().describe('Filter to specific asset symbols (e.g. ["USDC"], ["USDC","USDT","USDSUI"]). Case-insensitive.'),
|
|
1050
1224
|
stableOnly: z.boolean().optional().describe("When true, return only stablecoin markets (USDC, USDT, USDSUI, USDY, suiUSDT, etc.). Ignored when `assets` is supplied."),
|
|
@@ -1077,19 +1251,19 @@ var ratesInfoTool = buildTool({
|
|
|
1077
1251
|
topN: input.topN ?? 8
|
|
1078
1252
|
};
|
|
1079
1253
|
if (hasNaviMcpGlobal(context)) {
|
|
1080
|
-
const
|
|
1081
|
-
const
|
|
1082
|
-
return { data:
|
|
1254
|
+
const all = await fetchRates(getMcpManager(context));
|
|
1255
|
+
const filtered = applyFilters(all, opts);
|
|
1256
|
+
return { data: filtered, displayText: formatRatesSummary(filtered) };
|
|
1083
1257
|
}
|
|
1084
1258
|
if (hasAgent(context)) {
|
|
1085
1259
|
const agent = requireAgent(context);
|
|
1086
|
-
const
|
|
1087
|
-
const
|
|
1088
|
-
return { data:
|
|
1260
|
+
const all = await agent.rates();
|
|
1261
|
+
const filtered = applyFilters(all, opts);
|
|
1262
|
+
return { data: filtered, displayText: formatRatesSummary(filtered) };
|
|
1089
1263
|
}
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1264
|
+
throw new Error(
|
|
1265
|
+
"rates_info: NAVI lending data is currently unavailable. Try again shortly."
|
|
1266
|
+
);
|
|
1093
1267
|
}
|
|
1094
1268
|
});
|
|
1095
1269
|
function parseRpcTx(tx, address) {
|
|
@@ -1759,10 +1933,10 @@ Always use ISO-3166 country codes (GB not UK, US not USA). A return address ("fr
|
|
|
1759
1933
|
});
|
|
1760
1934
|
var MPP_GATEWAY2 = "https://mpp.t2000.ai";
|
|
1761
1935
|
var CATALOG_URL = `${MPP_GATEWAY2}/api/services`;
|
|
1762
|
-
var
|
|
1936
|
+
var CACHE_TTL = 12e4;
|
|
1763
1937
|
var catalogCache = null;
|
|
1764
1938
|
async function fetchCatalog() {
|
|
1765
|
-
if (catalogCache && Date.now() - catalogCache.ts <
|
|
1939
|
+
if (catalogCache && Date.now() - catalogCache.ts < CACHE_TTL) {
|
|
1766
1940
|
return catalogCache.data;
|
|
1767
1941
|
}
|
|
1768
1942
|
const res = await fetch(CATALOG_URL, { signal: AbortSignal.timeout(1e4) });
|
|
@@ -2256,11 +2430,31 @@ var portfolioAnalysisTool = buildTool({
|
|
|
2256
2430
|
if (!address) {
|
|
2257
2431
|
throw new Error("No wallet address provided. Sign in first.");
|
|
2258
2432
|
}
|
|
2259
|
-
const rpcUrl = context.suiRpcUrl ?? "https://fullnode.mainnet.sui.io:443";
|
|
2260
2433
|
const DUST_USD = 0.01;
|
|
2261
2434
|
const apiUrl = context.env?.AUDRIC_INTERNAL_API_URL;
|
|
2262
|
-
const [
|
|
2263
|
-
|
|
2435
|
+
const [portfolio, positions, weekHistResult] = await Promise.all([
|
|
2436
|
+
(async () => {
|
|
2437
|
+
if (context.portfolioCache) {
|
|
2438
|
+
const hit = context.portfolioCache.get(address);
|
|
2439
|
+
if (hit) return hit;
|
|
2440
|
+
}
|
|
2441
|
+
const fresh = await fetchAddressPortfolio(
|
|
2442
|
+
address,
|
|
2443
|
+
context.blockvisionApiKey,
|
|
2444
|
+
context.suiRpcUrl
|
|
2445
|
+
);
|
|
2446
|
+
context.portfolioCache?.set(address, fresh);
|
|
2447
|
+
return fresh;
|
|
2448
|
+
})().catch((err) => {
|
|
2449
|
+
console.warn("[portfolio_analysis] portfolio fetch failed:", err);
|
|
2450
|
+
const empty = {
|
|
2451
|
+
coins: [],
|
|
2452
|
+
totalUsd: 0,
|
|
2453
|
+
pricedAt: Date.now(),
|
|
2454
|
+
source: "sui-rpc-degraded"
|
|
2455
|
+
};
|
|
2456
|
+
return empty;
|
|
2457
|
+
}),
|
|
2264
2458
|
context.positionFetcher ? context.positionFetcher(address).catch((err) => {
|
|
2265
2459
|
console.warn("[portfolio_analysis] positionFetcher failed:", err);
|
|
2266
2460
|
return null;
|
|
@@ -2270,14 +2464,12 @@ var portfolioAnalysisTool = buildTool({
|
|
|
2270
2464
|
{ headers: { "x-sui-address": address }, signal: context.signal }
|
|
2271
2465
|
).then((res) => res.ok ? res.json() : null).catch(() => null) : Promise.resolve(null)
|
|
2272
2466
|
]);
|
|
2273
|
-
const nonZero = coins.filter((c) => Number(c.totalBalance) > 0);
|
|
2274
|
-
const prices = await fetchTokenPrices(nonZero.map((c) => c.coinType)).catch(() => ({}));
|
|
2275
2467
|
let walletValue = 0;
|
|
2276
2468
|
const allAllocations = [];
|
|
2277
|
-
for (const coin of
|
|
2278
|
-
const amount = Number(coin.
|
|
2279
|
-
|
|
2280
|
-
const usdValue = amount * price;
|
|
2469
|
+
for (const coin of portfolio.coins) {
|
|
2470
|
+
const amount = Number(coin.balance) / 10 ** coin.decimals;
|
|
2471
|
+
if (!Number.isFinite(amount) || amount <= 0) continue;
|
|
2472
|
+
const usdValue = coin.usdValue ?? (coin.price != null ? amount * coin.price : 0);
|
|
2281
2473
|
walletValue += usdValue;
|
|
2282
2474
|
allAllocations.push({ symbol: coin.symbol, amount, usdValue, percentage: 0 });
|
|
2283
2475
|
}
|
|
@@ -2349,7 +2541,8 @@ var portfolioAnalysisTool = buildTool({
|
|
|
2349
2541
|
insights,
|
|
2350
2542
|
savingsApy,
|
|
2351
2543
|
dailyEarning,
|
|
2352
|
-
weekChange
|
|
2544
|
+
weekChange,
|
|
2545
|
+
priceSource: portfolio.source
|
|
2353
2546
|
};
|
|
2354
2547
|
const topLine = `Total: $${totalValue.toFixed(2)} | Wallet: $${walletValue.toFixed(2)} | Savings: $${savingsValue.toFixed(2)}`;
|
|
2355
2548
|
const insightLines = insights.map((i) => `${i.type === "warning" ? "\u26A0" : "\u2192"} ${i.message}`).join("\n");
|
|
@@ -3095,335 +3288,59 @@ var activitySummaryTool = buildTool({
|
|
|
3095
3288
|
}
|
|
3096
3289
|
}
|
|
3097
3290
|
});
|
|
3098
|
-
var
|
|
3099
|
-
|
|
3100
|
-
|
|
3101
|
-
var CACHE_TTL3 = 6e4;
|
|
3102
|
-
var apiCache = /* @__PURE__ */ new Map();
|
|
3103
|
-
async function cachedFetch(url) {
|
|
3104
|
-
const hit = apiCache.get(url);
|
|
3105
|
-
if (hit && Date.now() - hit.ts < CACHE_TTL3) return hit.data;
|
|
3106
|
-
const res = await fetch(url, { signal: AbortSignal.timeout(15e3) });
|
|
3107
|
-
if (!res.ok) throw new Error(`DefiLlama API error: HTTP ${res.status}`);
|
|
3108
|
-
const data = await res.json();
|
|
3109
|
-
apiCache.set(url, { data, ts: Date.now() });
|
|
3110
|
-
return data;
|
|
3111
|
-
}
|
|
3112
|
-
async function fetchDefillamaYieldPools() {
|
|
3113
|
-
const data = await cachedFetch(`${YIELDS_API2}/pools`);
|
|
3114
|
-
return data.data ?? [];
|
|
3115
|
-
}
|
|
3116
|
-
function fmtToolTvl(tvl) {
|
|
3117
|
-
if (tvl >= 1e9) return `$${(tvl / 1e9).toFixed(1)}B`;
|
|
3118
|
-
if (tvl >= 1e6) return `$${(tvl / 1e6).toFixed(1)}M`;
|
|
3119
|
-
if (tvl >= 1e3) return `$${(tvl / 1e3).toFixed(0)}K`;
|
|
3120
|
-
return `$${tvl}`;
|
|
3121
|
-
}
|
|
3122
|
-
var POOL_STABLE_LEGS = /* @__PURE__ */ new Set([
|
|
3123
|
-
"USDC",
|
|
3124
|
-
"WUSDC",
|
|
3125
|
-
"USDT",
|
|
3126
|
-
"WUSDT",
|
|
3127
|
-
"SUIUSDT",
|
|
3128
|
-
"USDY",
|
|
3129
|
-
"USDSUI",
|
|
3130
|
-
"USDE",
|
|
3131
|
-
"AUSD",
|
|
3132
|
-
"FDUSD",
|
|
3133
|
-
"BUCK",
|
|
3134
|
-
"DAI",
|
|
3135
|
-
"LUSD",
|
|
3136
|
-
"FRAX",
|
|
3137
|
-
"GUSD",
|
|
3138
|
-
"PYUSD",
|
|
3139
|
-
"USDS",
|
|
3140
|
-
"CRVUSD"
|
|
3141
|
-
]);
|
|
3142
|
-
var defillamaYieldPoolsTool = buildTool({
|
|
3143
|
-
name: "defillama_yield_pools",
|
|
3144
|
-
description: 'Cross-protocol LP / vault yields with IMPERMANENT-LOSS RISK (Cetus, Bluefin, Full Sail, etc.). ONLY call when the user explicitly asks about LP pools, DeFi farming, or "higher yield with more risk". For safe single-sided lending yields (USDC save, NAVI, etc.) use rates_info instead \u2014 NEVER both in the same turn. Filter by chain (e.g. "Sui"), project, and minimum TVL.',
|
|
3291
|
+
var tokenPricesTool = buildTool({
|
|
3292
|
+
name: "token_prices",
|
|
3293
|
+
description: 'Get current USD prices for Sui tokens, with optional 24h change. Accepts full coin type strings (e.g. "0x2::sui::SUI"). Returns price per token and (when requested) 24h change percentage. Use for "what is X worth?" or "did Y move today?". For balance + portfolio rendering, prefer balance_check / portfolio_analysis instead \u2014 they bundle the same prices into the standard cards.',
|
|
3145
3294
|
inputSchema: z.object({
|
|
3146
|
-
|
|
3147
|
-
|
|
3148
|
-
limit: z.number().min(1).max(20).optional().describe("Max results (default 5)"),
|
|
3149
|
-
minTvl: z.number().optional().describe("Minimum TVL in USD to filter out small/risky pools (default 100000)"),
|
|
3150
|
-
stableOnly: z.boolean().optional().describe('When true, only return pools where every leg is a stablecoin (USDC, USDT, USDSUI, etc.). Use this for "show stablecoin yield options" \u2014 keeps volatile-pair LPs (WAL-SUI, DEEP-SUI) out.')
|
|
3295
|
+
coinTypes: z.array(z.string()).min(1).max(10).describe("Array of Sui coin type strings (max 10 per call)."),
|
|
3296
|
+
include24hChange: z.boolean().optional().describe("When true, include 24h change percentage per token in the output.")
|
|
3151
3297
|
}),
|
|
3152
3298
|
jsonSchema: {
|
|
3153
3299
|
type: "object",
|
|
3154
3300
|
properties: {
|
|
3155
|
-
|
|
3156
|
-
|
|
3157
|
-
|
|
3158
|
-
|
|
3159
|
-
|
|
3160
|
-
|
|
3161
|
-
|
|
3162
|
-
|
|
3163
|
-
isReadOnly: true,
|
|
3164
|
-
maxResultSizeChars: 6e3,
|
|
3165
|
-
async call(input) {
|
|
3166
|
-
if (!input.chain && !input.project) {
|
|
3167
|
-
const all = await fetchDefillamaYieldPools();
|
|
3168
|
-
const chainCounts = /* @__PURE__ */ new Map();
|
|
3169
|
-
for (const p of all) {
|
|
3170
|
-
chainCounts.set(p.chain, (chainCounts.get(p.chain) ?? 0) + 1);
|
|
3301
|
+
coinTypes: {
|
|
3302
|
+
type: "array",
|
|
3303
|
+
items: { type: "string" },
|
|
3304
|
+
description: "Sui coin type strings (max 10)."
|
|
3305
|
+
},
|
|
3306
|
+
include24hChange: {
|
|
3307
|
+
type: "boolean",
|
|
3308
|
+
description: "Include 24h change percentage per token."
|
|
3171
3309
|
}
|
|
3172
|
-
const topChains = [...chainCounts.entries()].sort((a, b) => b[1] - a[1]).slice(0, 8).map(([chain, count]) => ({ chain, pools: count }));
|
|
3173
|
-
return {
|
|
3174
|
-
data: {
|
|
3175
|
-
_refine: {
|
|
3176
|
-
reason: "Cross-chain yield search is too broad; pick a chain.",
|
|
3177
|
-
suggestedParams: { chain: "Sui" },
|
|
3178
|
-
availableChains: topChains
|
|
3179
|
-
}
|
|
3180
|
-
},
|
|
3181
|
-
displayText: "Yield query needs a chain filter. Common chains: " + topChains.map((c) => c.chain).join(", ")
|
|
3182
|
-
};
|
|
3183
|
-
}
|
|
3184
|
-
let pools = await fetchDefillamaYieldPools();
|
|
3185
|
-
if (input.chain) {
|
|
3186
|
-
const chain = input.chain.toLowerCase();
|
|
3187
|
-
pools = pools.filter((p) => p.chain.toLowerCase() === chain);
|
|
3188
|
-
}
|
|
3189
|
-
if (input.project) {
|
|
3190
|
-
const project = input.project.toLowerCase();
|
|
3191
|
-
pools = pools.filter((p) => p.project.toLowerCase() === project);
|
|
3192
|
-
}
|
|
3193
|
-
const minTvl = input.minTvl ?? 1e5;
|
|
3194
|
-
pools = pools.filter((p) => p.tvlUsd >= minTvl);
|
|
3195
|
-
if (input.stableOnly) {
|
|
3196
|
-
pools = pools.filter((p) => {
|
|
3197
|
-
const legs = p.symbol.split("-");
|
|
3198
|
-
return legs.every((leg) => POOL_STABLE_LEGS.has(leg.trim().toUpperCase()));
|
|
3199
|
-
});
|
|
3200
|
-
}
|
|
3201
|
-
pools.sort((a, b) => b.apy - a.apy);
|
|
3202
|
-
const limit = input.limit ?? 5;
|
|
3203
|
-
const top = pools.slice(0, limit);
|
|
3204
|
-
const results = top.map((p) => ({
|
|
3205
|
-
pool: p.symbol,
|
|
3206
|
-
protocol: p.project,
|
|
3207
|
-
chain: p.chain,
|
|
3208
|
-
apy: Math.round(p.apy * 100) / 100,
|
|
3209
|
-
apyBase: p.apyBase != null ? Math.round(p.apyBase * 100) / 100 : void 0,
|
|
3210
|
-
apyReward: p.apyReward != null ? Math.round(p.apyReward * 100) / 100 : void 0,
|
|
3211
|
-
tvl: Math.round(p.tvlUsd)
|
|
3212
|
-
}));
|
|
3213
|
-
return {
|
|
3214
|
-
data: results,
|
|
3215
|
-
displayText: results.map((r) => `${r.pool} (${r.protocol}): ${r.apy}% APY, ${fmtToolTvl(r.tvl)} TVL`).join("\n")
|
|
3216
|
-
};
|
|
3217
|
-
}
|
|
3218
|
-
});
|
|
3219
|
-
var defillamaProtocolInfoTool = buildTool({
|
|
3220
|
-
name: "defillama_protocol_info",
|
|
3221
|
-
description: 'Get detailed info about a DeFi protocol: TVL, category, chains it operates on, and TVL changes. Use for "Is this protocol safe?" or "Tell me about NAVI."',
|
|
3222
|
-
inputSchema: z.object({
|
|
3223
|
-
name: z.string().describe('Protocol name (e.g. "navi-lending", "cetus")')
|
|
3224
|
-
}),
|
|
3225
|
-
jsonSchema: {
|
|
3226
|
-
type: "object",
|
|
3227
|
-
properties: {
|
|
3228
|
-
name: { type: "string", description: 'Protocol slug (e.g. "navi-lending")' }
|
|
3229
|
-
},
|
|
3230
|
-
required: ["name"]
|
|
3231
|
-
},
|
|
3232
|
-
isReadOnly: true,
|
|
3233
|
-
maxResultSizeChars: 4e3,
|
|
3234
|
-
async call(input) {
|
|
3235
|
-
const data = await cachedFetch(`${LLAMA_API2}/protocol/${encodeURIComponent(input.name)}`);
|
|
3236
|
-
const result = {
|
|
3237
|
-
name: data.name,
|
|
3238
|
-
category: data.category,
|
|
3239
|
-
chains: data.chains,
|
|
3240
|
-
tvl: Math.round(data.tvl),
|
|
3241
|
-
change1d: data.change_1d,
|
|
3242
|
-
change7d: data.change_7d,
|
|
3243
|
-
url: data.url,
|
|
3244
|
-
description: data.description
|
|
3245
|
-
};
|
|
3246
|
-
return {
|
|
3247
|
-
data: result,
|
|
3248
|
-
displayText: `${result.name}: ${fmtToolTvl(result.tvl)} TVL (${result.category}) on ${result.chains.join(", ")}`
|
|
3249
|
-
};
|
|
3250
|
-
}
|
|
3251
|
-
});
|
|
3252
|
-
var defillamaTokenPricesTool = buildTool({
|
|
3253
|
-
name: "defillama_token_prices",
|
|
3254
|
-
description: 'Get current USD prices for Sui tokens. Accepts full coin type strings (e.g. "0x2::sui::SUI"). Returns price per token.',
|
|
3255
|
-
inputSchema: z.object({
|
|
3256
|
-
coinTypes: z.array(z.string()).min(1).max(10).describe("Array of Sui coin type strings")
|
|
3257
|
-
}),
|
|
3258
|
-
jsonSchema: {
|
|
3259
|
-
type: "object",
|
|
3260
|
-
properties: {
|
|
3261
|
-
coinTypes: { type: "array", items: { type: "string" }, description: "Sui coin type strings" }
|
|
3262
3310
|
},
|
|
3263
3311
|
required: ["coinTypes"]
|
|
3264
3312
|
},
|
|
3265
3313
|
isReadOnly: true,
|
|
3266
|
-
async call(input) {
|
|
3267
|
-
const prices = await fetchTokenPrices(input.coinTypes);
|
|
3268
|
-
const results = input.coinTypes.map((
|
|
3269
|
-
coinType
|
|
3270
|
-
symbol
|
|
3271
|
-
|
|
3272
|
-
|
|
3273
|
-
|
|
3274
|
-
|
|
3275
|
-
|
|
3276
|
-
|
|
3277
|
-
|
|
3278
|
-
}
|
|
3279
|
-
|
|
3280
|
-
|
|
3281
|
-
description: "Get price change for a Sui token over a period. Shows current price and historical price to calculate % change.",
|
|
3282
|
-
inputSchema: z.object({
|
|
3283
|
-
coinType: z.string().describe('Sui coin type (e.g. "0x2::sui::SUI")'),
|
|
3284
|
-
period: z.enum(["1h", "24h", "7d", "30d"]).optional().describe('Period (default "24h")')
|
|
3285
|
-
}),
|
|
3286
|
-
jsonSchema: {
|
|
3287
|
-
type: "object",
|
|
3288
|
-
properties: {
|
|
3289
|
-
coinType: { type: "string", description: "Sui coin type string" },
|
|
3290
|
-
period: { type: "string", description: "Period: 1h, 24h, 7d, 30d" }
|
|
3291
|
-
},
|
|
3292
|
-
required: ["coinType"]
|
|
3293
|
-
},
|
|
3294
|
-
isReadOnly: true,
|
|
3295
|
-
async call(input) {
|
|
3296
|
-
const period = input.period ?? "24h";
|
|
3297
|
-
const hoursMap = { "1h": 1, "24h": 24, "7d": 168, "30d": 720 };
|
|
3298
|
-
const hours = hoursMap[period] ?? 24;
|
|
3299
|
-
const historicalTs = Math.floor(Date.now() / 1e3) - hours * 3600;
|
|
3300
|
-
const coinKey = `sui:${input.coinType}`;
|
|
3301
|
-
const [current, historical] = await Promise.all([
|
|
3302
|
-
cachedFetch(
|
|
3303
|
-
`${COINS_API}/prices/current/${encodeURIComponent(coinKey)}`
|
|
3304
|
-
),
|
|
3305
|
-
cachedFetch(
|
|
3306
|
-
`${COINS_API}/prices/historical/${historicalTs}/${encodeURIComponent(coinKey)}`
|
|
3307
|
-
)
|
|
3308
|
-
]);
|
|
3309
|
-
const currentPrice = current.coins[coinKey]?.price;
|
|
3310
|
-
const historicalPrice = historical.coins[coinKey]?.price;
|
|
3311
|
-
const symbol = input.coinType.split("::").pop() ?? input.coinType;
|
|
3312
|
-
if (currentPrice == null) {
|
|
3313
|
-
return {
|
|
3314
|
-
data: { symbol, currentPrice: 0, historicalPrice: null, change: null, period },
|
|
3315
|
-
displayText: "Token price not available on DefiLlama."
|
|
3316
|
-
};
|
|
3317
|
-
}
|
|
3318
|
-
const change = historicalPrice ? Math.round((currentPrice - historicalPrice) / historicalPrice * 1e4) / 100 : null;
|
|
3319
|
-
return {
|
|
3320
|
-
data: {
|
|
3314
|
+
async call(input, context) {
|
|
3315
|
+
const prices = await fetchTokenPrices(input.coinTypes, context.blockvisionApiKey);
|
|
3316
|
+
const results = input.coinTypes.map((coinType) => {
|
|
3317
|
+
const entry = prices[coinType];
|
|
3318
|
+
const symbol = coinType.split("::").pop() ?? coinType;
|
|
3319
|
+
if (!entry) {
|
|
3320
|
+
return {
|
|
3321
|
+
coinType,
|
|
3322
|
+
symbol,
|
|
3323
|
+
price: null,
|
|
3324
|
+
priceUnavailable: true
|
|
3325
|
+
};
|
|
3326
|
+
}
|
|
3327
|
+
const out = {
|
|
3328
|
+
coinType,
|
|
3321
3329
|
symbol,
|
|
3322
|
-
|
|
3323
|
-
|
|
3324
|
-
|
|
3325
|
-
|
|
3326
|
-
}
|
|
3327
|
-
|
|
3328
|
-
};
|
|
3329
|
-
}
|
|
3330
|
-
});
|
|
3331
|
-
var defillamaChainTvlTool = buildTool({
|
|
3332
|
-
name: "defillama_chain_tvl",
|
|
3333
|
-
description: 'Get chain TVL rankings. Shows top chains by total value locked. Use for "How big is Sui?" or "Compare chains."',
|
|
3334
|
-
inputSchema: z.object({
|
|
3335
|
-
limit: z.number().min(1).max(20).optional().describe("Max results (default 10)")
|
|
3336
|
-
}),
|
|
3337
|
-
jsonSchema: {
|
|
3338
|
-
type: "object",
|
|
3339
|
-
properties: {
|
|
3340
|
-
limit: { type: "number", description: "Max results (default 10)" }
|
|
3341
|
-
},
|
|
3342
|
-
required: []
|
|
3343
|
-
},
|
|
3344
|
-
isReadOnly: true,
|
|
3345
|
-
async call(input) {
|
|
3346
|
-
const data = await cachedFetch(`${LLAMA_API2}/v2/chains`);
|
|
3347
|
-
const sorted = [...data].sort((a, b) => b.tvl - a.tvl);
|
|
3348
|
-
const limit = input.limit ?? 10;
|
|
3349
|
-
const top = sorted.slice(0, limit);
|
|
3350
|
-
const results = top.map((c, i) => ({
|
|
3351
|
-
rank: i + 1,
|
|
3352
|
-
chain: c.name,
|
|
3353
|
-
tvl: Math.round(c.tvl)
|
|
3354
|
-
}));
|
|
3355
|
-
return {
|
|
3356
|
-
data: results,
|
|
3357
|
-
displayText: results.map((r) => `#${r.rank} ${r.chain}: $${(r.tvl / 1e9).toFixed(2)}B`).join("\n")
|
|
3358
|
-
};
|
|
3359
|
-
}
|
|
3360
|
-
});
|
|
3361
|
-
var defillamaProtocolFeesTool = buildTool({
|
|
3362
|
-
name: "defillama_protocol_fees",
|
|
3363
|
-
description: 'Get protocol fee/revenue rankings. Shows which protocols earn the most in fees. Use for "Which protocols are most profitable?"',
|
|
3364
|
-
inputSchema: z.object({
|
|
3365
|
-
chain: z.string().optional().describe("Filter by chain"),
|
|
3366
|
-
limit: z.number().min(1).max(20).optional().describe("Max results (default 5)")
|
|
3367
|
-
}),
|
|
3368
|
-
jsonSchema: {
|
|
3369
|
-
type: "object",
|
|
3370
|
-
properties: {
|
|
3371
|
-
chain: { type: "string", description: "Filter by chain" },
|
|
3372
|
-
limit: { type: "number", description: "Max results (default 5)" }
|
|
3373
|
-
},
|
|
3374
|
-
required: []
|
|
3375
|
-
},
|
|
3376
|
-
isReadOnly: true,
|
|
3377
|
-
async call(input) {
|
|
3378
|
-
const data = await cachedFetch(`${LLAMA_API2}/overview/fees`);
|
|
3379
|
-
let protocols = data.protocols ?? [];
|
|
3380
|
-
if (input.chain) {
|
|
3381
|
-
const chain = input.chain.toLowerCase();
|
|
3382
|
-
protocols = protocols.filter(
|
|
3383
|
-
(p) => p.chains?.some((c) => c.toLowerCase() === chain)
|
|
3384
|
-
);
|
|
3385
|
-
}
|
|
3386
|
-
protocols.sort((a, b) => (b.total24h ?? 0) - (a.total24h ?? 0));
|
|
3387
|
-
const limit = input.limit ?? 5;
|
|
3388
|
-
const top = protocols.slice(0, limit);
|
|
3389
|
-
const results = top.map((p) => ({
|
|
3390
|
-
name: p.name,
|
|
3391
|
-
fees24h: p.total24h != null ? Math.round(p.total24h) : null,
|
|
3392
|
-
fees7d: p.total7d != null ? Math.round(p.total7d) : null,
|
|
3393
|
-
category: p.category
|
|
3394
|
-
}));
|
|
3395
|
-
return {
|
|
3396
|
-
data: results,
|
|
3397
|
-
displayText: results.map((r) => `${r.name}: $${r.fees24h != null ? (r.fees24h / 1e3).toFixed(1) + "K" : "?"}/day`).join("\n")
|
|
3398
|
-
};
|
|
3399
|
-
}
|
|
3400
|
-
});
|
|
3401
|
-
var defillamaSuiProtocolsTool = buildTool({
|
|
3402
|
-
name: "defillama_sui_protocols",
|
|
3403
|
-
description: "List top DeFi protocols on Sui by TVL. Shows name, TVL, category, and slug for each protocol. Use to discover protocols before calling defillama_protocol_info.",
|
|
3404
|
-
inputSchema: z.object({
|
|
3405
|
-
limit: z.number().int().min(1).max(50).optional().describe("Max protocols to return (default 10)")
|
|
3406
|
-
}),
|
|
3407
|
-
jsonSchema: {
|
|
3408
|
-
type: "object",
|
|
3409
|
-
properties: {
|
|
3410
|
-
limit: { type: "number", description: "Max protocols to return (default 10)" }
|
|
3411
|
-
}
|
|
3412
|
-
},
|
|
3413
|
-
isReadOnly: true,
|
|
3414
|
-
async call(input) {
|
|
3415
|
-
const limit = input.limit ?? 10;
|
|
3416
|
-
const data = await cachedFetch(`${LLAMA_API2}/protocols`);
|
|
3417
|
-
const suiProtocols = data.filter((p) => p.chains?.includes("Sui") && p.tvl > 0).sort((a, b) => b.tvl - a.tvl).slice(0, limit);
|
|
3418
|
-
const results = suiProtocols.map((p) => ({
|
|
3419
|
-
name: p.name,
|
|
3420
|
-
slug: p.slug,
|
|
3421
|
-
tvl: Math.round(p.tvl),
|
|
3422
|
-
category: p.category
|
|
3423
|
-
}));
|
|
3330
|
+
price: entry.price
|
|
3331
|
+
};
|
|
3332
|
+
if (input.include24hChange && entry.change24h !== void 0) {
|
|
3333
|
+
out.change24h = entry.change24h;
|
|
3334
|
+
}
|
|
3335
|
+
return out;
|
|
3336
|
+
});
|
|
3424
3337
|
return {
|
|
3425
3338
|
data: results,
|
|
3426
|
-
displayText: results.map((r
|
|
3339
|
+
displayText: results.map((r) => {
|
|
3340
|
+
if (r.price === null) return `${r.symbol}: price unavailable`;
|
|
3341
|
+
const change = r.change24h;
|
|
3342
|
+
return change !== void 0 ? `${r.symbol}: $${r.price.toFixed(4)} (${change >= 0 ? "+" : ""}${change.toFixed(2)}% 24h)` : `${r.symbol}: $${r.price.toFixed(4)}`;
|
|
3343
|
+
}).join(", ")
|
|
3427
3344
|
};
|
|
3428
3345
|
}
|
|
3429
3346
|
});
|
|
@@ -3443,13 +3360,7 @@ var READ_TOOLS = [
|
|
|
3443
3360
|
explainTxTool,
|
|
3444
3361
|
portfolioAnalysisTool,
|
|
3445
3362
|
protocolDeepDiveTool,
|
|
3446
|
-
|
|
3447
|
-
defillamaProtocolInfoTool,
|
|
3448
|
-
defillamaTokenPricesTool,
|
|
3449
|
-
defillamaPriceChangeTool,
|
|
3450
|
-
defillamaChainTvlTool,
|
|
3451
|
-
defillamaProtocolFeesTool,
|
|
3452
|
-
defillamaSuiProtocolsTool,
|
|
3363
|
+
tokenPricesTool,
|
|
3453
3364
|
listPaymentLinksTool,
|
|
3454
3365
|
cancelPaymentLinkTool,
|
|
3455
3366
|
listInvoicesTool,
|
|
@@ -3512,7 +3423,7 @@ function getModifiableFields(toolName) {
|
|
|
3512
3423
|
}
|
|
3513
3424
|
|
|
3514
3425
|
// src/prompt.ts
|
|
3515
|
-
var DEFAULT_SYSTEM_PROMPT = `You are Audric \u2014 a financial agent on Sui. Audric is exactly five products: Audric Passport (the trust layer \u2014 Google sign-in, non-custodial wallet, tap-to-confirm consent, sponsored gas \u2014 wraps every other product), Audric Intelligence (you \u2014 the 5-system brain: Agent Harness with
|
|
3426
|
+
var DEFAULT_SYSTEM_PROMPT = `You are Audric \u2014 a financial agent on Sui. Audric is exactly five products: Audric Passport (the trust layer \u2014 Google sign-in, non-custodial wallet, tap-to-confirm consent, sponsored gas \u2014 wraps every other product), Audric Intelligence (you \u2014 the 5-system brain: Agent Harness with 34 tools, Reasoning Engine with 9 guards and 7 skill recipes, Silent Profile, Chain Memory, AdviceLog), Audric Finance (manage money on Sui \u2014 Save via NAVI lending at 3-8% APY USDC, Credit via NAVI borrowing with health factor, Swap via Cetus aggregator across 20+ DEXs at 0.1% fee, Charts for yield/health/portfolio viz), Audric Pay (move money \u2014 send USDC, receive via payment links / invoices / QR; free, global, instant on Sui), and Audric Store (creator marketplace, ships Phase 5 \u2014 say "coming soon" if asked). Save, swap, borrow, repay, withdraw, charts \u2192 Audric Finance. Send, receive, payment-link, invoice, QR \u2192 Audric Pay. Your silent context (profile, memory, chain facts, advice log) shapes your replies but never surfaces as a notification \u2014 you act only when the user asks, and every write waits on their tap-to-confirm via Passport. You can also call 41 paid APIs (music, image, research, translation, weather, fulfilment) via MPP micropayments using the pay_api tool \u2014 this is an internal capability, not a promoted product, so only mention it when the user asks for something that needs it.
|
|
3516
3427
|
|
|
3517
3428
|
## Response rules
|
|
3518
3429
|
- 1-2 sentences max. No bullet lists unless asked. No preambles.
|
|
@@ -3532,8 +3443,8 @@ Only offer to execute actions you have tools for. If you retrieved a quote, data
|
|
|
3532
3443
|
## Tool usage
|
|
3533
3444
|
- Use tools proactively \u2014 don't refuse requests you can handle.
|
|
3534
3445
|
- For real-world questions (weather, search, news, prices), use pay_api. Tell the user the cost first.
|
|
3535
|
-
- For
|
|
3536
|
-
-
|
|
3446
|
+
- For NAVI lending APYs, use rates_info; for VOLO liquid staking stats, use volo_stats; for spot token prices, use token_prices.
|
|
3447
|
+
- For protocol-level due diligence (TVL, fees, audits, safety) on Sui DeFi protocols, use protocol_deep_dive with the slug.
|
|
3537
3448
|
- Run multiple read-only tools in parallel when you need several data points.
|
|
3538
3449
|
- If a tool errors, say what went wrong and what to try instead. One sentence.
|
|
3539
3450
|
|
|
@@ -3546,11 +3457,11 @@ Only offer to execute actions you have tools for. If you retrieved a quote, data
|
|
|
3546
3457
|
## Multi-step flows
|
|
3547
3458
|
- "How much X for Y?": swap_quote first, then swap_execute if user confirms.
|
|
3548
3459
|
- "Swap then save": swap_execute \u2192 balance_check \u2192 save_deposit. Confirm each step.
|
|
3549
|
-
- "Buy $X of token":
|
|
3550
|
-
- "Best yield on SUI": compare rates_info (NAVI lending) +
|
|
3460
|
+
- "Buy $X of token": token_prices \u2192 calculate amount \u2192 swap_execute.
|
|
3461
|
+
- "Best yield on SUI": compare rates_info (NAVI lending) + volo_stats (vSUI liquid staking).
|
|
3551
3462
|
- withdraw supports legacy positions: USDC, USDe, USDsui, SUI. Pass asset param to withdraw a specific token.
|
|
3552
3463
|
- "Deposit SUI to earn yield": volo_stake for SUI liquid staking. save_deposit is USDC only.
|
|
3553
|
-
- "
|
|
3464
|
+
- "Is protocol X safe?" / "Tell me about NAVI": protocol_deep_dive with the slug.
|
|
3554
3465
|
- "Full account report" / "account summary" / "give me everything" / "complete overview": triggers the \`account_report\` recipe \u2014 when the recipe block appears, follow EVERY step including all six tool calls. Each step renders a distinct rich card; skipping a step means a missing card.
|
|
3555
3466
|
|
|
3556
3467
|
## Safety
|
|
@@ -4701,6 +4612,12 @@ var QueryEngine = class {
|
|
|
4701
4612
|
sessionSpendUsd;
|
|
4702
4613
|
onAutoExecuted;
|
|
4703
4614
|
onGuardFired;
|
|
4615
|
+
// [v1.4 BlockVision] BlockVision Indexer API key + per-request portfolio
|
|
4616
|
+
// cache. Forwarded into every `ToolContext` build site so read tools
|
|
4617
|
+
// (`balance_check`, `portfolio_analysis`, future `token_prices`) hit the
|
|
4618
|
+
// shared host-paid endpoint and dedupe across each other within a turn.
|
|
4619
|
+
blockvisionApiKey;
|
|
4620
|
+
portfolioCache;
|
|
4704
4621
|
// [v1.5] See `EngineConfig.postWriteRefresh` — drives the post-write
|
|
4705
4622
|
// synthetic read injection in `resumeWithToolResult`.
|
|
4706
4623
|
postWriteRefresh;
|
|
@@ -4750,6 +4667,8 @@ var QueryEngine = class {
|
|
|
4750
4667
|
this.onAutoExecuted = config.onAutoExecuted;
|
|
4751
4668
|
this.onGuardFired = config.onGuardFired;
|
|
4752
4669
|
this.postWriteRefresh = config.postWriteRefresh;
|
|
4670
|
+
this.blockvisionApiKey = config.blockvisionApiKey;
|
|
4671
|
+
this.portfolioCache = config.portfolioCache;
|
|
4753
4672
|
this.tools = config.tools ?? (config.agent ? getDefaultTools() : []);
|
|
4754
4673
|
}
|
|
4755
4674
|
/**
|
|
@@ -4867,8 +4786,14 @@ var QueryEngine = class {
|
|
|
4867
4786
|
signal,
|
|
4868
4787
|
priceCache: this.priceCache,
|
|
4869
4788
|
permissionConfig: this.permissionConfig,
|
|
4870
|
-
sessionSpendUsd: this.sessionSpendUsd
|
|
4789
|
+
sessionSpendUsd: this.sessionSpendUsd,
|
|
4790
|
+
blockvisionApiKey: this.blockvisionApiKey,
|
|
4791
|
+
portfolioCache: this.portfolioCache
|
|
4871
4792
|
};
|
|
4793
|
+
if (this.walletAddress) {
|
|
4794
|
+
this.portfolioCache?.delete(this.walletAddress);
|
|
4795
|
+
clearPortfolioCacheFor(this.walletAddress);
|
|
4796
|
+
}
|
|
4872
4797
|
if (!signal.aborted) {
|
|
4873
4798
|
await new Promise((resolve) => {
|
|
4874
4799
|
const t = setTimeout(resolve, 1500);
|
|
@@ -5014,7 +4939,9 @@ var QueryEngine = class {
|
|
|
5014
4939
|
signal,
|
|
5015
4940
|
priceCache: this.priceCache,
|
|
5016
4941
|
permissionConfig: this.permissionConfig,
|
|
5017
|
-
sessionSpendUsd: this.sessionSpendUsd
|
|
4942
|
+
sessionSpendUsd: this.sessionSpendUsd,
|
|
4943
|
+
blockvisionApiKey: this.blockvisionApiKey,
|
|
4944
|
+
portfolioCache: this.portfolioCache
|
|
5018
4945
|
};
|
|
5019
4946
|
try {
|
|
5020
4947
|
const result = await tool.call(parsed.data, context);
|
|
@@ -5057,7 +4984,9 @@ var QueryEngine = class {
|
|
|
5057
4984
|
signal,
|
|
5058
4985
|
priceCache: this.priceCache,
|
|
5059
4986
|
permissionConfig: this.permissionConfig,
|
|
5060
|
-
sessionSpendUsd: this.sessionSpendUsd
|
|
4987
|
+
sessionSpendUsd: this.sessionSpendUsd,
|
|
4988
|
+
blockvisionApiKey: this.blockvisionApiKey,
|
|
4989
|
+
portfolioCache: this.portfolioCache
|
|
5061
4990
|
};
|
|
5062
4991
|
let turns = 0;
|
|
5063
4992
|
let hasRetriedWithCleanHistory = false;
|
|
@@ -5421,7 +5350,8 @@ ${recipeCtx}`;
|
|
|
5421
5350
|
);
|
|
5422
5351
|
Promise.resolve().then(() => this.onAutoExecuted({
|
|
5423
5352
|
toolName: toolEvent.toolName,
|
|
5424
|
-
usdValue
|
|
5353
|
+
usdValue,
|
|
5354
|
+
walletAddress: this.walletAddress
|
|
5425
5355
|
})).catch((err) => {
|
|
5426
5356
|
console.warn("[engine] onAutoExecuted callback failed:", err);
|
|
5427
5357
|
});
|
|
@@ -5476,6 +5406,7 @@ ${recipeCtx}`;
|
|
|
5476
5406
|
const writeGuardInjections = pendingWrite.call._guardInjections;
|
|
5477
5407
|
const modifiableFields = getModifiableFields(pendingWrite.call.name);
|
|
5478
5408
|
const turnIndex = this.messages.filter((m) => m.role === "assistant").length;
|
|
5409
|
+
const attemptId = randomUUID();
|
|
5479
5410
|
this.turnPaused = true;
|
|
5480
5411
|
yield {
|
|
5481
5412
|
type: "pending_action",
|
|
@@ -5492,7 +5423,8 @@ ${recipeCtx}`;
|
|
|
5492
5423
|
})),
|
|
5493
5424
|
...writeGuardInjections?.length ? { guardInjections: writeGuardInjections } : {},
|
|
5494
5425
|
...modifiableFields?.length ? { modifiableFields } : {},
|
|
5495
|
-
turnIndex
|
|
5426
|
+
turnIndex,
|
|
5427
|
+
attemptId
|
|
5496
5428
|
}
|
|
5497
5429
|
};
|
|
5498
5430
|
return;
|
|
@@ -6748,6 +6680,6 @@ function sanitizeAnthropicMessages(messages) {
|
|
|
6748
6680
|
return merged;
|
|
6749
6681
|
}
|
|
6750
6682
|
|
|
6751
|
-
export { AnthropicProvider, BalanceTracker, CANVAS_TEMPLATES, ContextBudget, CostTracker, DEFAULT_GUARD_CONFIG, DEFAULT_PERMISSION_CONFIG, DEFAULT_SYSTEM_PROMPT, EarlyToolDispatcher, McpClientManager, McpResponseCache, MemorySessionStore, NAVI_MCP_CONFIG, NAVI_MCP_URL, NAVI_SERVER_NAME, NaviTools, PERMISSION_PRESETS, QueryEngine, READ_TOOLS, RecipeRegistry, RetryTracker, TOOL_FLAGS, TOOL_MODIFIABLE_FIELDS, TxMutex, WRITE_TOOLS, activitySummaryTool, adaptAllMcpTools, adaptAllServerTools, adaptMcpTool, applyToolFlags, balanceCheckTool, borrowTool, budgetToolResult, buildCachedSystemPrompt, buildMcpTools, buildProactivenessInstructions, buildProfileContext, buildSelfEvaluationInstruction, buildStateContext, buildTool, claimRewardsTool, classifyEffort,
|
|
6683
|
+
export { AnthropicProvider, BalanceTracker, CANVAS_TEMPLATES, ContextBudget, CostTracker, DEFAULT_GUARD_CONFIG, DEFAULT_PERMISSION_CONFIG, DEFAULT_SYSTEM_PROMPT, EarlyToolDispatcher, McpClientManager, McpResponseCache, MemorySessionStore, NAVI_MCP_CONFIG, NAVI_MCP_URL, NAVI_SERVER_NAME, NaviTools, PERMISSION_PRESETS, QueryEngine, READ_TOOLS, RecipeRegistry, RetryTracker, TOOL_FLAGS, TOOL_MODIFIABLE_FIELDS, TxMutex, WRITE_TOOLS, activitySummaryTool, adaptAllMcpTools, adaptAllServerTools, adaptMcpTool, applyToolFlags, balanceCheckTool, borrowTool, budgetToolResult, buildCachedSystemPrompt, buildMcpTools, buildProactivenessInstructions, buildProfileContext, buildSelfEvaluationInstruction, buildStateContext, buildTool, claimRewardsTool, classifyEffort, clearPortfolioCache, clearPortfolioCacheFor, clearPriceMapCache, compactMessages, createGuardRunnerState, engineToSSE, estimateTokens, explainTxTool, extractConversationText, extractMcpText, fetchAddressPortfolio, fetchAvailableRewards, fetchBalance, fetchHealthFactor, fetchPositions, fetchProtocolStats, fetchRates, fetchSavings, fetchTokenPrices, fetchWalletCoins, findTool, getDefaultTools, getMcpManager, getModifiableFields, getToolFlags, getWalletAddress, guardArtifactPreview, guardStaleData, hasNaviMcp, healthCheckTool, loadRecipes, microcompact, mppServicesTool, parseMcpJson, parseRecipe, parseSSE, payApiTool, portfolioAnalysisTool, protocolDeepDiveTool, ratesInfoTool, registerEngineTools, renderCanvasTool, repayDebtTool, requireAgent, resolvePermissionTier, resolveUsdValue, runGuards, runTools, saveContactTool, saveDepositTool, savingsInfoTool, sendTransferTool, serializeSSE, spendingAnalyticsTool, swapExecuteTool, swapQuoteTool, tokenPricesTool, toolNameToOperation, toolsToDefinitions, transactionHistoryTool, transformBalance, transformHealthFactor, transformPositions, transformRates, transformRewards, transformSavings, updateGuardStateAfterToolResult, validateHistory, voloStakeTool, voloStatsTool, voloUnstakeTool, webSearchTool, withdrawTool, yieldSummaryTool };
|
|
6752
6684
|
//# sourceMappingURL=index.js.map
|
|
6753
6685
|
//# sourceMappingURL=index.js.map
|