@t2000/engine 1.1.2 → 1.2.1
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.d.ts +119 -1
- package/dist/index.js +352 -96
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1642,9 +1642,96 @@ async function fetchAudricHistory(address, opts, env, signal) {
|
|
|
1642
1642
|
}
|
|
1643
1643
|
}
|
|
1644
1644
|
|
|
1645
|
+
// src/sui-address.ts
|
|
1646
|
+
var SUI_MAINNET_URL2 = "https://fullnode.mainnet.sui.io:443";
|
|
1647
|
+
var SUI_ADDRESS_REGEX = /^0x[a-fA-F0-9]{1,64}$/i;
|
|
1648
|
+
var SUI_ADDRESS_STRICT_REGEX = /^0x[a-fA-F0-9]{64}$/i;
|
|
1649
|
+
var SUINS_NAME_REGEX = /^[a-z0-9-]+(\.[a-z0-9-]+)*\.sui$/;
|
|
1650
|
+
var InvalidAddressError = class extends Error {
|
|
1651
|
+
constructor(raw) {
|
|
1652
|
+
super(
|
|
1653
|
+
`"${raw}" isn't a valid Sui address or SuiNS name. Pass a 0x-prefixed hex address (e.g. 0x40cd\u20263e62) or a SuiNS name ending in .sui (e.g. alex.sui).`
|
|
1654
|
+
);
|
|
1655
|
+
this.raw = raw;
|
|
1656
|
+
this.name = "InvalidAddressError";
|
|
1657
|
+
}
|
|
1658
|
+
};
|
|
1659
|
+
var SuinsNotRegisteredError = class extends Error {
|
|
1660
|
+
constructor(name_) {
|
|
1661
|
+
super(
|
|
1662
|
+
`"${name_}" isn't a registered SuiNS name. Double-check the spelling, or paste the full Sui address (0x\u2026 64 hex characters).`
|
|
1663
|
+
);
|
|
1664
|
+
this.name_ = name_;
|
|
1665
|
+
this.name = "SuinsNotRegisteredError";
|
|
1666
|
+
}
|
|
1667
|
+
};
|
|
1668
|
+
var SuinsRpcError = class extends Error {
|
|
1669
|
+
constructor(name_, detail) {
|
|
1670
|
+
super(`SuiNS lookup failed for "${name_}" (${detail}). Try again, or paste the full Sui address.`);
|
|
1671
|
+
this.name_ = name_;
|
|
1672
|
+
this.name = "SuinsRpcError";
|
|
1673
|
+
}
|
|
1674
|
+
};
|
|
1675
|
+
function looksLikeSuiNs(value) {
|
|
1676
|
+
if (!value) return false;
|
|
1677
|
+
return SUINS_NAME_REGEX.test(value.trim().toLowerCase());
|
|
1678
|
+
}
|
|
1679
|
+
async function resolveSuinsViaRpc(rawName, ctx = {}) {
|
|
1680
|
+
const name = rawName.trim().toLowerCase();
|
|
1681
|
+
if (!SUINS_NAME_REGEX.test(name)) {
|
|
1682
|
+
throw new InvalidAddressError(rawName);
|
|
1683
|
+
}
|
|
1684
|
+
const url = ctx.suiRpcUrl || SUI_MAINNET_URL2;
|
|
1685
|
+
let res;
|
|
1686
|
+
try {
|
|
1687
|
+
res = await fetch(url, {
|
|
1688
|
+
method: "POST",
|
|
1689
|
+
headers: { "Content-Type": "application/json" },
|
|
1690
|
+
body: JSON.stringify({
|
|
1691
|
+
jsonrpc: "2.0",
|
|
1692
|
+
id: 1,
|
|
1693
|
+
method: "suix_resolveNameServiceAddress",
|
|
1694
|
+
params: [name]
|
|
1695
|
+
}),
|
|
1696
|
+
signal: ctx.signal ?? AbortSignal.timeout(8e3)
|
|
1697
|
+
});
|
|
1698
|
+
} catch (err) {
|
|
1699
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1700
|
+
throw new SuinsRpcError(name, msg);
|
|
1701
|
+
}
|
|
1702
|
+
if (!res.ok) {
|
|
1703
|
+
throw new SuinsRpcError(name, `HTTP ${res.status}`);
|
|
1704
|
+
}
|
|
1705
|
+
let body;
|
|
1706
|
+
try {
|
|
1707
|
+
body = await res.json();
|
|
1708
|
+
} catch (err) {
|
|
1709
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1710
|
+
throw new SuinsRpcError(name, `JSON parse failed: ${msg}`);
|
|
1711
|
+
}
|
|
1712
|
+
if (body.error) {
|
|
1713
|
+
throw new SuinsRpcError(name, body.error.message);
|
|
1714
|
+
}
|
|
1715
|
+
return body.result ?? null;
|
|
1716
|
+
}
|
|
1717
|
+
async function normalizeAddressInput(value, ctx = {}) {
|
|
1718
|
+
const trimmed = value.trim();
|
|
1719
|
+
if (SUI_ADDRESS_REGEX.test(trimmed)) {
|
|
1720
|
+
return { address: trimmed.toLowerCase(), suinsName: null, raw: value };
|
|
1721
|
+
}
|
|
1722
|
+
if (looksLikeSuiNs(trimmed)) {
|
|
1723
|
+
const name = trimmed.toLowerCase();
|
|
1724
|
+
const address = await resolveSuinsViaRpc(name, ctx);
|
|
1725
|
+
if (!address) {
|
|
1726
|
+
throw new SuinsNotRegisteredError(name);
|
|
1727
|
+
}
|
|
1728
|
+
return { address: address.toLowerCase(), suinsName: name, raw: value };
|
|
1729
|
+
}
|
|
1730
|
+
throw new InvalidAddressError(value);
|
|
1731
|
+
}
|
|
1732
|
+
|
|
1645
1733
|
// src/tools/balance.ts
|
|
1646
1734
|
var GAS_RESERVE_SUI2 = 0.05;
|
|
1647
|
-
var SUI_ADDRESS_REGEX = /^0x[a-fA-F0-9]{1,64}$/;
|
|
1648
1735
|
var VSUI_COIN_TYPE = "0x549e8b69270defbfafd4f94e17ec44cdbdd99820b33bda2278dea3b9a32d3f55::cert::CERT";
|
|
1649
1736
|
var SUI_COIN_TYPE = "0x2::sui::SUI";
|
|
1650
1737
|
var VSUI_FALLBACK_RATE = 1.05;
|
|
@@ -1696,17 +1783,16 @@ async function loadPortfolio(address, blockvisionApiKey, fallbackRpcUrl, cache)
|
|
|
1696
1783
|
}
|
|
1697
1784
|
var balanceCheckTool = buildTool({
|
|
1698
1785
|
name: "balance_check",
|
|
1699
|
-
description:
|
|
1786
|
+
description: 'Get the full balance breakdown for the signed-in user OR any public Sui address or SuiNS name. Returns wallet holdings (tokens the address owns \u2014 NOT savings), NAVI savings deposits (USDC and/or USDsui deposited into NAVI Protocol earning yield), outstanding debt, pending rewards, gas reserve, total net worth, saveableUsdc (USDC wallet balance available to save), and saveableUsdsui (USDsui wallet balance available to save \u2014 surfaces only when > 0). IMPORTANT: wallet holdings like GOLD, SUI, USDT, USDe are NOT savings positions and are NOT saveable \u2014 only USDC and USDsui can be saved/borrowed. Pass `address` as a 0x address OR a SuiNS name (e.g. "alex.sui") to inspect a contact / watched / public wallet; defaults to the signed-in user when omitted.',
|
|
1700
1787
|
inputSchema: z.object({
|
|
1701
|
-
address: z.string().
|
|
1788
|
+
address: z.string().optional().describe("Sui address (0x\u2026) or SuiNS name (alex.sui). Defaults to the signed-in wallet when omitted.")
|
|
1702
1789
|
}),
|
|
1703
1790
|
jsonSchema: {
|
|
1704
1791
|
type: "object",
|
|
1705
1792
|
properties: {
|
|
1706
1793
|
address: {
|
|
1707
1794
|
type: "string",
|
|
1708
|
-
|
|
1709
|
-
description: "Sui address to inspect (defaults to the signed-in wallet)"
|
|
1795
|
+
description: "Sui address (0x\u2026) or SuiNS name (e.g. alex.sui). The engine resolves the name to an on-chain address before querying. Omit to default to the signed-in wallet."
|
|
1710
1796
|
}
|
|
1711
1797
|
},
|
|
1712
1798
|
required: []
|
|
@@ -1718,7 +1804,18 @@ var balanceCheckTool = buildTool({
|
|
|
1718
1804
|
// calls — each one reflects a different on-chain + market snapshot.
|
|
1719
1805
|
cacheable: false,
|
|
1720
1806
|
async call(input, context) {
|
|
1721
|
-
|
|
1807
|
+
let suinsName = null;
|
|
1808
|
+
let targetAddress;
|
|
1809
|
+
if (input.address) {
|
|
1810
|
+
const normalized = await normalizeAddressInput(input.address, {
|
|
1811
|
+
suiRpcUrl: context.suiRpcUrl,
|
|
1812
|
+
signal: context.signal
|
|
1813
|
+
});
|
|
1814
|
+
targetAddress = normalized.address;
|
|
1815
|
+
suinsName = normalized.suinsName;
|
|
1816
|
+
} else {
|
|
1817
|
+
targetAddress = context.walletAddress;
|
|
1818
|
+
}
|
|
1722
1819
|
const isSelfQuery = !!context.walletAddress && !!targetAddress && targetAddress.toLowerCase() === context.walletAddress.toLowerCase();
|
|
1723
1820
|
if (hasNaviMcpGlobal(context)) {
|
|
1724
1821
|
if (!targetAddress) {
|
|
@@ -1850,10 +1947,12 @@ var balanceCheckTool = buildTool({
|
|
|
1850
1947
|
saveableUsdsui,
|
|
1851
1948
|
priceSource: portfolio.source,
|
|
1852
1949
|
address,
|
|
1853
|
-
isSelfQuery
|
|
1950
|
+
isSelfQuery,
|
|
1951
|
+
suinsName
|
|
1854
1952
|
};
|
|
1855
1953
|
const holdingsList = visibleHoldings.map((h) => `${h.symbol}: ${h.balance < 1 ? h.balance.toFixed(6) : h.balance.toFixed(2)} ($${h.usdValue.toFixed(2)})`).join(", ");
|
|
1856
|
-
const
|
|
1954
|
+
const subjectLabel = suinsName ?? `${address.slice(0, 6)}\u2026${address.slice(-4)}`;
|
|
1955
|
+
const subjectPrefix = isSelfQuery ? "Balance" : `Balance for ${subjectLabel}`;
|
|
1857
1956
|
const defiSummaryText = (() => {
|
|
1858
1957
|
if (defi2.source === "degraded") {
|
|
1859
1958
|
return ' DeFi positions (Bluefin / Suilend / Cetus / etc.): UNAVAILABLE \u2014 DeFi data source is currently unreachable. Do NOT assert "no DeFi positions"; tell the user this slice is temporarily unknown and the total above EXCLUDES DeFi.';
|
|
@@ -1877,9 +1976,9 @@ var balanceCheckTool = buildTool({
|
|
|
1877
1976
|
displayText: `${subjectPrefix}: $${bal.total.toFixed(2)} total. Wallet holdings (NOT savings): ${holdingsList || "none"}. NAVI savings deposits: $${bal.savings.toFixed(2)}.${defiSummaryText} ${saveableSummary}`
|
|
1878
1977
|
};
|
|
1879
1978
|
}
|
|
1880
|
-
if (
|
|
1979
|
+
if (targetAddress && context.walletAddress && targetAddress.toLowerCase() !== context.walletAddress.toLowerCase()) {
|
|
1881
1980
|
throw new Error(
|
|
1882
|
-
`Cannot inspect ${
|
|
1981
|
+
`Cannot inspect ${targetAddress.slice(0, 8)}\u2026 without NAVI MCP enabled. Configure NAVI MCP to enable third-party address reads.`
|
|
1883
1982
|
);
|
|
1884
1983
|
}
|
|
1885
1984
|
const agent = requireAgent(context);
|
|
@@ -1941,7 +2040,8 @@ var balanceCheckTool = buildTool({
|
|
|
1941
2040
|
saveableUsdc: sdkSaveableUsdc,
|
|
1942
2041
|
saveableUsdsui: sdkSaveableUsdsui,
|
|
1943
2042
|
address: targetAddress ?? "",
|
|
1944
|
-
isSelfQuery: true
|
|
2043
|
+
isSelfQuery: true,
|
|
2044
|
+
suinsName
|
|
1945
2045
|
},
|
|
1946
2046
|
displayText: `Balance: $${sdkTotal.toFixed(2)} total. Wallet: $${balance.available.toFixed(2)} available. NAVI savings deposits: $${balance.savings.toFixed(2)}.${sdkDefiSummaryText} ${sdkSaveableUsdsui > 0 ? `Saveable: ${sdkSaveableUsdc.toFixed(2)} USDC + ${sdkSaveableUsdsui.toFixed(sdkSaveableUsdsui < 1 ? 4 : 2)} USDsui (only USDC and USDsui can be saved/borrowed).` : `Saveable USDC (only USDC and USDsui can be saved): ${sdkSaveableUsdc.toFixed(2)} USDC.`}`
|
|
1947
2047
|
};
|
|
@@ -2173,7 +2273,6 @@ async function fetchProtocolStats(manager, opts) {
|
|
|
2173
2273
|
|
|
2174
2274
|
// src/tools/savings.ts
|
|
2175
2275
|
var DUST_THRESHOLD_USD = 0.01;
|
|
2176
|
-
var SUI_ADDRESS_REGEX2 = /^0x[a-fA-F0-9]{1,64}$/;
|
|
2177
2276
|
function buildSavingsFromPositions(sp) {
|
|
2178
2277
|
const positions = [
|
|
2179
2278
|
...sp.supplies.filter((s) => s.amountUsd >= DUST_THRESHOLD_USD).map((s) => ({
|
|
@@ -2215,11 +2314,12 @@ function buildSavingsFromPositions(sp) {
|
|
|
2215
2314
|
}
|
|
2216
2315
|
};
|
|
2217
2316
|
}
|
|
2218
|
-
function formatSavingsDisplay(result, isSelfQuery = true, address) {
|
|
2317
|
+
function formatSavingsDisplay(result, isSelfQuery = true, address, suinsName) {
|
|
2219
2318
|
const { positions, earnings, fundStatus } = result;
|
|
2220
2319
|
const supplies = positions.filter((p) => p.type === "supply");
|
|
2221
2320
|
const borrows = positions.filter((p) => p.type === "borrow");
|
|
2222
|
-
const
|
|
2321
|
+
const subjectLabel = suinsName ?? (address ? `${address.slice(0, 6)}\u2026${address.slice(-4)}` : null);
|
|
2322
|
+
const subjectPrefix = isSelfQuery || !subjectLabel ? "" : `${subjectLabel} \u2014 `;
|
|
2223
2323
|
const lines = [];
|
|
2224
2324
|
if (supplies.length > 0) {
|
|
2225
2325
|
lines.push(`${subjectPrefix}Savings: $${fundStatus.supplied.toFixed(2)} at ${(earnings.currentApy * 100).toFixed(2)}% blended APY`);
|
|
@@ -2239,17 +2339,16 @@ function formatSavingsDisplay(result, isSelfQuery = true, address) {
|
|
|
2239
2339
|
}
|
|
2240
2340
|
var savingsInfoTool = buildTool({
|
|
2241
2341
|
name: "savings_info",
|
|
2242
|
-
description:
|
|
2342
|
+
description: 'Get detailed savings positions and earnings for the signed-in user OR any public Sui address or SuiNS name: current deposits by protocol, APY, total yield earned, daily earning rate, and projected monthly returns. Pass `address` as a 0x address OR a SuiNS name (e.g. "alex.sui") to inspect a contact / watched / public wallet; defaults to the signed-in user when omitted.',
|
|
2243
2343
|
inputSchema: z.object({
|
|
2244
|
-
address: z.string().
|
|
2344
|
+
address: z.string().optional().describe("Sui address (0x\u2026) or SuiNS name (alex.sui). Defaults to the signed-in wallet when omitted.")
|
|
2245
2345
|
}),
|
|
2246
2346
|
jsonSchema: {
|
|
2247
2347
|
type: "object",
|
|
2248
2348
|
properties: {
|
|
2249
2349
|
address: {
|
|
2250
2350
|
type: "string",
|
|
2251
|
-
|
|
2252
|
-
description: "Sui address to inspect (defaults to the signed-in wallet)"
|
|
2351
|
+
description: "Sui address (0x\u2026) or SuiNS name (e.g. alex.sui). The engine resolves the name to an on-chain address before querying. Omit to default to the signed-in wallet."
|
|
2253
2352
|
}
|
|
2254
2353
|
},
|
|
2255
2354
|
required: []
|
|
@@ -2259,23 +2358,34 @@ var savingsInfoTool = buildTool({
|
|
|
2259
2358
|
// Each call reflects a fresh on-chain snapshot — never dedupe.
|
|
2260
2359
|
cacheable: false,
|
|
2261
2360
|
async call(input, context) {
|
|
2262
|
-
|
|
2361
|
+
let suinsName = null;
|
|
2362
|
+
let targetAddress;
|
|
2363
|
+
if (input.address) {
|
|
2364
|
+
const normalized = await normalizeAddressInput(input.address, {
|
|
2365
|
+
suiRpcUrl: context.suiRpcUrl,
|
|
2366
|
+
signal: context.signal
|
|
2367
|
+
});
|
|
2368
|
+
targetAddress = normalized.address;
|
|
2369
|
+
suinsName = normalized.suinsName;
|
|
2370
|
+
} else {
|
|
2371
|
+
targetAddress = context.walletAddress;
|
|
2372
|
+
}
|
|
2263
2373
|
const isSelfQuery = !!context.walletAddress && !!targetAddress && targetAddress.toLowerCase() === context.walletAddress.toLowerCase();
|
|
2264
2374
|
if (context.positionFetcher && targetAddress) {
|
|
2265
2375
|
const sp = await context.positionFetcher(targetAddress);
|
|
2266
2376
|
const result2 = buildSavingsFromPositions(sp);
|
|
2267
|
-
const stamped2 = { ...result2, address: targetAddress, isSelfQuery };
|
|
2268
|
-
return { data: stamped2, displayText: formatSavingsDisplay(result2, isSelfQuery, targetAddress) };
|
|
2377
|
+
const stamped2 = { ...result2, address: targetAddress, isSelfQuery, suinsName };
|
|
2378
|
+
return { data: stamped2, displayText: formatSavingsDisplay(result2, isSelfQuery, targetAddress, suinsName) };
|
|
2269
2379
|
}
|
|
2270
2380
|
if (hasNaviMcpGlobal(context) && targetAddress) {
|
|
2271
2381
|
const savings = await fetchSavings(getMcpManager(context), targetAddress);
|
|
2272
2382
|
savings.positions = savings.positions.filter((p) => p.valueUsd >= DUST_THRESHOLD_USD);
|
|
2273
|
-
const stamped2 = { ...savings, address: targetAddress, isSelfQuery };
|
|
2274
|
-
return { data: stamped2, displayText: formatSavingsDisplay(savings, isSelfQuery, targetAddress) };
|
|
2383
|
+
const stamped2 = { ...savings, address: targetAddress, isSelfQuery, suinsName };
|
|
2384
|
+
return { data: stamped2, displayText: formatSavingsDisplay(savings, isSelfQuery, targetAddress, suinsName) };
|
|
2275
2385
|
}
|
|
2276
|
-
if (
|
|
2386
|
+
if (targetAddress && context.walletAddress && targetAddress.toLowerCase() !== context.walletAddress.toLowerCase()) {
|
|
2277
2387
|
throw new Error(
|
|
2278
|
-
`Cannot inspect ${
|
|
2388
|
+
`Cannot inspect ${targetAddress.slice(0, 8)}\u2026 without NAVI MCP or a positionFetcher. Configure NAVI MCP to enable third-party address reads.`
|
|
2279
2389
|
);
|
|
2280
2390
|
}
|
|
2281
2391
|
const agent = requireAgent(context);
|
|
@@ -2309,11 +2419,10 @@ var savingsInfoTool = buildTool({
|
|
|
2309
2419
|
projectedMonthly: fundStatus.projectedMonthly
|
|
2310
2420
|
}
|
|
2311
2421
|
};
|
|
2312
|
-
const stamped = { ...result, address: targetAddress ?? "", isSelfQuery: true };
|
|
2313
|
-
return { data: stamped, displayText: formatSavingsDisplay(result, true, void 0) };
|
|
2422
|
+
const stamped = { ...result, address: targetAddress ?? "", isSelfQuery: true, suinsName };
|
|
2423
|
+
return { data: stamped, displayText: formatSavingsDisplay(result, true, void 0, suinsName) };
|
|
2314
2424
|
}
|
|
2315
2425
|
});
|
|
2316
|
-
var SUI_ADDRESS_REGEX3 = /^0x[a-fA-F0-9]{1,64}$/;
|
|
2317
2426
|
var DEBT_DUST_USD = 0.01;
|
|
2318
2427
|
function hfStatus(hf, borrowed) {
|
|
2319
2428
|
if (borrowed <= DEBT_DUST_USD) return "healthy";
|
|
@@ -2327,8 +2436,9 @@ function serializeHf(hf, borrowed) {
|
|
|
2327
2436
|
if (hf == null || !Number.isFinite(hf)) return null;
|
|
2328
2437
|
return hf;
|
|
2329
2438
|
}
|
|
2330
|
-
function displayHfText(hf, borrowed, status, isSelfQuery = true, address) {
|
|
2331
|
-
const
|
|
2439
|
+
function displayHfText(hf, borrowed, status, isSelfQuery = true, address, suinsName) {
|
|
2440
|
+
const subjectLabel = suinsName ?? (address ? `${address.slice(0, 6)}\u2026${address.slice(-4)}` : null);
|
|
2441
|
+
const subject = isSelfQuery || !subjectLabel ? "Health Factor" : `Health Factor for ${subjectLabel}`;
|
|
2332
2442
|
if (hf == null) {
|
|
2333
2443
|
return `${subject}: \u221E (${status} \u2014 no debt)`;
|
|
2334
2444
|
}
|
|
@@ -2336,17 +2446,16 @@ function displayHfText(hf, borrowed, status, isSelfQuery = true, address) {
|
|
|
2336
2446
|
}
|
|
2337
2447
|
var healthCheckTool = buildTool({
|
|
2338
2448
|
name: "health_check",
|
|
2339
|
-
description: 'Check the lending health factor for the signed-in user OR any public Sui address: current HF ratio, total supplied collateral, total borrowed, max additional borrow capacity, and liquidation threshold. HF < 1.5 is risky, < 1.2 is critical. When the address has no debt the tool returns healthFactor=null (semantically infinity) \u2014 render that as "Healthy" / \u221E, never as 0 or "Critical". Pass `address` to inspect a contact / watched / public wallet; defaults to the signed-in user when omitted.',
|
|
2449
|
+
description: 'Check the lending health factor for the signed-in user OR any public Sui address or SuiNS name: current HF ratio, total supplied collateral, total borrowed, max additional borrow capacity, and liquidation threshold. HF < 1.5 is risky, < 1.2 is critical. When the address has no debt the tool returns healthFactor=null (semantically infinity) \u2014 render that as "Healthy" / \u221E, never as 0 or "Critical". Pass `address` as a 0x address OR a SuiNS name (e.g. "alex.sui") to inspect a contact / watched / public wallet; defaults to the signed-in user when omitted.',
|
|
2340
2450
|
inputSchema: z.object({
|
|
2341
|
-
address: z.string().
|
|
2451
|
+
address: z.string().optional().describe("Sui address (0x\u2026) or SuiNS name (alex.sui). Defaults to the signed-in wallet when omitted.")
|
|
2342
2452
|
}),
|
|
2343
2453
|
jsonSchema: {
|
|
2344
2454
|
type: "object",
|
|
2345
2455
|
properties: {
|
|
2346
2456
|
address: {
|
|
2347
2457
|
type: "string",
|
|
2348
|
-
|
|
2349
|
-
description: "Sui address to inspect (defaults to the signed-in wallet)"
|
|
2458
|
+
description: "Sui address (0x\u2026) or SuiNS name (e.g. alex.sui). The engine resolves the name to an on-chain address before querying. Omit to default to the signed-in wallet."
|
|
2350
2459
|
}
|
|
2351
2460
|
},
|
|
2352
2461
|
required: []
|
|
@@ -2356,7 +2465,18 @@ var healthCheckTool = buildTool({
|
|
|
2356
2465
|
// movement and even passively as oracle prices update. Never dedupe.
|
|
2357
2466
|
cacheable: false,
|
|
2358
2467
|
async call(input, context) {
|
|
2359
|
-
|
|
2468
|
+
let suinsName = null;
|
|
2469
|
+
let targetAddress;
|
|
2470
|
+
if (input.address) {
|
|
2471
|
+
const normalized = await normalizeAddressInput(input.address, {
|
|
2472
|
+
suiRpcUrl: context.suiRpcUrl,
|
|
2473
|
+
signal: context.signal
|
|
2474
|
+
});
|
|
2475
|
+
targetAddress = normalized.address;
|
|
2476
|
+
suinsName = normalized.suinsName;
|
|
2477
|
+
} else {
|
|
2478
|
+
targetAddress = context.walletAddress;
|
|
2479
|
+
}
|
|
2360
2480
|
const isSelfQuery = !!context.walletAddress && !!targetAddress && targetAddress.toLowerCase() === context.walletAddress.toLowerCase();
|
|
2361
2481
|
if (context.positionFetcher && targetAddress) {
|
|
2362
2482
|
const sp = await context.positionFetcher(targetAddress);
|
|
@@ -2373,9 +2493,10 @@ var healthCheckTool = buildTool({
|
|
|
2373
2493
|
liquidationThreshold: 0,
|
|
2374
2494
|
status: status2,
|
|
2375
2495
|
address: targetAddress,
|
|
2376
|
-
isSelfQuery
|
|
2496
|
+
isSelfQuery,
|
|
2497
|
+
suinsName
|
|
2377
2498
|
},
|
|
2378
|
-
displayText: displayHfText(transportHf2, borrowed2, status2, isSelfQuery, targetAddress)
|
|
2499
|
+
displayText: displayHfText(transportHf2, borrowed2, status2, isSelfQuery, targetAddress, suinsName)
|
|
2379
2500
|
};
|
|
2380
2501
|
}
|
|
2381
2502
|
if (hasNaviMcpGlobal(context) && targetAddress) {
|
|
@@ -2384,13 +2505,13 @@ var healthCheckTool = buildTool({
|
|
|
2384
2505
|
const status2 = hfStatus(hf2.healthFactor, borrowed2);
|
|
2385
2506
|
const transportHf2 = serializeHf(hf2.healthFactor, borrowed2);
|
|
2386
2507
|
return {
|
|
2387
|
-
data: { ...hf2, healthFactor: transportHf2, status: status2, address: targetAddress, isSelfQuery },
|
|
2388
|
-
displayText: displayHfText(transportHf2, borrowed2, status2, isSelfQuery, targetAddress)
|
|
2508
|
+
data: { ...hf2, healthFactor: transportHf2, status: status2, address: targetAddress, isSelfQuery, suinsName },
|
|
2509
|
+
displayText: displayHfText(transportHf2, borrowed2, status2, isSelfQuery, targetAddress, suinsName)
|
|
2389
2510
|
};
|
|
2390
2511
|
}
|
|
2391
|
-
if (
|
|
2512
|
+
if (targetAddress && context.walletAddress && targetAddress.toLowerCase() !== context.walletAddress.toLowerCase()) {
|
|
2392
2513
|
throw new Error(
|
|
2393
|
-
`Cannot inspect ${
|
|
2514
|
+
`Cannot inspect ${targetAddress.slice(0, 8)}\u2026 without NAVI MCP or a positionFetcher. Configure NAVI MCP to enable third-party address reads.`
|
|
2394
2515
|
);
|
|
2395
2516
|
}
|
|
2396
2517
|
const agent = requireAgent(context);
|
|
@@ -2407,9 +2528,10 @@ var healthCheckTool = buildTool({
|
|
|
2407
2528
|
liquidationThreshold: hf.liquidationThreshold,
|
|
2408
2529
|
status,
|
|
2409
2530
|
address: targetAddress ?? "",
|
|
2410
|
-
isSelfQuery: true
|
|
2531
|
+
isSelfQuery: true,
|
|
2532
|
+
suinsName
|
|
2411
2533
|
},
|
|
2412
|
-
displayText: displayHfText(transportHf, borrowed, status, true, void 0)
|
|
2534
|
+
displayText: displayHfText(transportHf, borrowed, status, true, void 0, suinsName)
|
|
2413
2535
|
};
|
|
2414
2536
|
}
|
|
2415
2537
|
});
|
|
@@ -2633,14 +2755,13 @@ async function queryHistoryByDate(rpcUrl, address, targetDate, limit) {
|
|
|
2633
2755
|
}
|
|
2634
2756
|
var HISTORY_ACTIONS = ["send", "lending", "swap", "transaction"];
|
|
2635
2757
|
var DEFAULT_LOOKBACK_DAYS = 30;
|
|
2636
|
-
var SUI_ADDRESS_REGEX4 = /^0x[0-9a-fA-F]{64}$/;
|
|
2637
2758
|
var transactionHistoryTool = buildTool({
|
|
2638
2759
|
name: "transaction_history",
|
|
2639
2760
|
description: 'Retrieve recent transaction history (last 30 days by default): sends, saves, withdrawals, borrows, repayments, swaps, and rewards claims. Renders a rich transaction card.\n\nBy default, queries the SIGNED-IN USER\'S history. To inspect another wallet (a saved contact, a watched address, any public Sui address), pass `address` \u2014 e.g. user asks "show funkii\'s recent transactions" with funkii at 0x40cd\u20263e62, call with `address: "0x40cd\u20263e62"`. To filter the user\'s own history to a specific counterparty (user asks "show transactions WITH funkii"), pass `counterparty` \u2014 keeps the query rooted in the user\'s wallet but shows only rows where funkii is the recipient or sender.\n\nFilter args: `date` (YYYY-MM-DD), `action` (send/lending/swap), `minUsd` (minimum amount in USD \u2014 use this for "transactions over $X" instead of post-filtering), `assetSymbol` (e.g. "USDC", "SUI"), `direction` ("in" or "out"). The card itself respects all filters \u2014 never re-list the rows in narration.\n\nInternally queries both `FromAddress` and `ToAddress` filters in parallel and dedupes by digest, so pure-receive transactions (someone sends to the queried address with no balance-affecting outbound) are no longer dropped.',
|
|
2640
2761
|
inputSchema: z.object({
|
|
2641
2762
|
limit: z.number().int().min(1).max(50).optional(),
|
|
2642
|
-
address: z.string().
|
|
2643
|
-
counterparty: z.string().
|
|
2763
|
+
address: z.string().optional().describe(`Sui address (0x\u2026) or SuiNS name (e.g. "alex.sui") to query history FOR. When omitted, defaults to the signed-in user's wallet. Pass this when the user asks about a contact's, watched address's, or any other public wallet's history.`),
|
|
2764
|
+
counterparty: z.string().optional().describe('Sui address (0x\u2026) or SuiNS name (e.g. "alex.sui") to filter rows by \u2014 only transactions where the queried address sent to or received from this counterparty are returned. Use for "show transactions with <contact>" queries. Compares against `tx.recipient` (case-insensitive).'),
|
|
2644
2765
|
date: z.string().optional().describe("Specific date to search for transactions (YYYY-MM-DD format). Paginates back to find that day."),
|
|
2645
2766
|
action: z.enum(HISTORY_ACTIONS).optional().describe("Filter by action: send, lending, swap, or transaction."),
|
|
2646
2767
|
minUsd: z.number().min(0).optional().describe('Minimum transaction amount in USD. Use this for "transactions over $X" \u2014 the amount is converted to USD using the asset price snapshot.'),
|
|
@@ -2656,11 +2777,11 @@ var transactionHistoryTool = buildTool({
|
|
|
2656
2777
|
},
|
|
2657
2778
|
address: {
|
|
2658
2779
|
type: "string",
|
|
2659
|
-
description: "Sui address to query history FOR (defaults to the signed-in user when omitted). Use for queries about a contact's, watched address's, or any other wallet's history."
|
|
2780
|
+
description: "Sui address (0x\u2026) or SuiNS name (e.g. alex.sui) to query history FOR (defaults to the signed-in user when omitted). The engine resolves SuiNS names to addresses before querying. Use for queries about a contact's, watched address's, or any other wallet's history."
|
|
2660
2781
|
},
|
|
2661
2782
|
counterparty: {
|
|
2662
2783
|
type: "string",
|
|
2663
|
-
description: 'Sui address to filter rows by \u2014 only transactions where the queried address sent to or received from this counterparty are returned. Use for "show transactions with <contact>" queries.'
|
|
2784
|
+
description: 'Sui address (0x\u2026) or SuiNS name (e.g. alex.sui) to filter rows by \u2014 only transactions where the queried address sent to or received from this counterparty are returned. Use for "show transactions with <contact>" queries.'
|
|
2664
2785
|
},
|
|
2665
2786
|
date: {
|
|
2666
2787
|
type: "string",
|
|
@@ -2734,8 +2855,20 @@ var transactionHistoryTool = buildTool({
|
|
|
2734
2855
|
const assetSymbol = input.assetSymbol?.toLowerCase();
|
|
2735
2856
|
const direction = input.direction;
|
|
2736
2857
|
const minUsd = input.minUsd;
|
|
2737
|
-
const
|
|
2738
|
-
|
|
2858
|
+
const [normalizedAddress, normalizedCounterparty] = await Promise.all([
|
|
2859
|
+
input.address ? normalizeAddressInput(input.address, {
|
|
2860
|
+
suiRpcUrl: context.suiRpcUrl,
|
|
2861
|
+
signal: context.signal
|
|
2862
|
+
}) : Promise.resolve(null),
|
|
2863
|
+
input.counterparty ? normalizeAddressInput(input.counterparty, {
|
|
2864
|
+
suiRpcUrl: context.suiRpcUrl,
|
|
2865
|
+
signal: context.signal
|
|
2866
|
+
}) : Promise.resolve(null)
|
|
2867
|
+
]);
|
|
2868
|
+
const suinsName = normalizedAddress?.suinsName ?? null;
|
|
2869
|
+
const counterpartySuinsName = normalizedCounterparty?.suinsName ?? null;
|
|
2870
|
+
const counterpartyLower = normalizedCounterparty?.address.toLowerCase();
|
|
2871
|
+
const targetAddress = normalizedAddress?.address ?? context.walletAddress;
|
|
2739
2872
|
const isSelfQuery = !!targetAddress && !!context.walletAddress && targetAddress.toLowerCase() === context.walletAddress.toLowerCase();
|
|
2740
2873
|
const prices = context.tokenPrices;
|
|
2741
2874
|
const priceFor2 = (sym) => {
|
|
@@ -2772,9 +2905,13 @@ var transactionHistoryTool = buildTool({
|
|
|
2772
2905
|
minUsd: minUsd ?? null,
|
|
2773
2906
|
assetSymbol: input.assetSymbol ?? null,
|
|
2774
2907
|
direction: direction ?? null,
|
|
2775
|
-
counterparty
|
|
2908
|
+
// Pre-resolved counterparty address (for the LLM/UI). Original
|
|
2909
|
+
// SuiNS name is preserved in `counterpartySuinsName` below.
|
|
2910
|
+
counterparty: normalizedCounterparty?.address ?? null,
|
|
2911
|
+
counterpartySuinsName,
|
|
2776
2912
|
address: targetAddress ?? null,
|
|
2777
|
-
isSelfQuery
|
|
2913
|
+
isSelfQuery,
|
|
2914
|
+
suinsName
|
|
2778
2915
|
};
|
|
2779
2916
|
if (context.agent) {
|
|
2780
2917
|
if (input.address && !isSelfQuery) {
|
|
@@ -3761,23 +3898,37 @@ ${summary}`
|
|
|
3761
3898
|
}
|
|
3762
3899
|
});
|
|
3763
3900
|
var inputSchema3 = z.object({
|
|
3764
|
-
address: z.string().optional().describe("Sui address to analyze
|
|
3901
|
+
address: z.string().optional().describe("Sui address (0x\u2026) or SuiNS name (alex.sui) to analyze. Defaults to the signed-in wallet when omitted.")
|
|
3765
3902
|
});
|
|
3766
3903
|
var STABLECOINS = /* @__PURE__ */ new Set(["USDC", "USDT", "USDe", "USDsui"]);
|
|
3767
3904
|
var portfolioAnalysisTool = buildTool({
|
|
3768
3905
|
name: "portfolio_analysis",
|
|
3769
|
-
description:
|
|
3906
|
+
description: 'Analyze portfolio allocation, risk exposure, and yield optimization for the signed-in user OR any public Sui address or SuiNS name. Shows asset breakdown, diversification score, health factor assessment, and actionable suggestions. Pass `address` as a 0x address OR a SuiNS name (e.g. "alex.sui") to analyze a contact / watched / public wallet.',
|
|
3770
3907
|
inputSchema: inputSchema3,
|
|
3771
3908
|
jsonSchema: {
|
|
3772
3909
|
type: "object",
|
|
3773
3910
|
properties: {
|
|
3774
|
-
address: {
|
|
3911
|
+
address: {
|
|
3912
|
+
type: "string",
|
|
3913
|
+
description: "Sui address (0x\u2026) or SuiNS name (e.g. alex.sui). The engine resolves the name to an on-chain address before querying. Omit to default to the signed-in wallet."
|
|
3914
|
+
}
|
|
3775
3915
|
},
|
|
3776
3916
|
required: []
|
|
3777
3917
|
},
|
|
3778
3918
|
isReadOnly: true,
|
|
3779
3919
|
async call(input, context) {
|
|
3780
|
-
|
|
3920
|
+
let suinsName = null;
|
|
3921
|
+
let address;
|
|
3922
|
+
if (input.address) {
|
|
3923
|
+
const normalized = await normalizeAddressInput(input.address, {
|
|
3924
|
+
suiRpcUrl: context.suiRpcUrl,
|
|
3925
|
+
signal: context.signal
|
|
3926
|
+
});
|
|
3927
|
+
address = normalized.address;
|
|
3928
|
+
suinsName = normalized.suinsName;
|
|
3929
|
+
} else {
|
|
3930
|
+
address = context.walletAddress;
|
|
3931
|
+
}
|
|
3781
3932
|
if (!address) {
|
|
3782
3933
|
throw new Error("No wallet address provided. Sign in first.");
|
|
3783
3934
|
}
|
|
@@ -3956,7 +4107,10 @@ var portfolioAnalysisTool = buildTool({
|
|
|
3956
4107
|
savingsApy,
|
|
3957
4108
|
dailyEarning,
|
|
3958
4109
|
weekChange,
|
|
3959
|
-
priceSource: portfolio.source
|
|
4110
|
+
priceSource: portfolio.source,
|
|
4111
|
+
address,
|
|
4112
|
+
isSelfQuery: !!context.walletAddress && address.toLowerCase() === context.walletAddress.toLowerCase(),
|
|
4113
|
+
suinsName
|
|
3960
4114
|
};
|
|
3961
4115
|
const defiSegment = defiValue > 0 ? ` | DeFi: $${defiValue.toFixed(2)}${defiSummary.source === "partial" ? " (partial)" : ""}` : "";
|
|
3962
4116
|
const topLine = `Total: $${totalValue.toFixed(2)} | Wallet: $${walletValue.toFixed(2)} | Savings: $${savingsValue.toFixed(2)}${defiSegment}`;
|
|
@@ -4422,15 +4576,34 @@ Always prefer the canvas for visualisation requests. After rendering, offer to e
|
|
|
4422
4576
|
async call(input, context) {
|
|
4423
4577
|
const { template, params } = input;
|
|
4424
4578
|
const title = CANVAS_TITLES[template];
|
|
4579
|
+
const ADDRESS_AWARE_TEMPLATES = /* @__PURE__ */ new Set([
|
|
4580
|
+
"full_portfolio",
|
|
4581
|
+
"watch_address",
|
|
4582
|
+
"portfolio_timeline",
|
|
4583
|
+
"spending_breakdown",
|
|
4584
|
+
"activity_heatmap",
|
|
4585
|
+
"health_simulator"
|
|
4586
|
+
]);
|
|
4587
|
+
let suinsName = null;
|
|
4588
|
+
let resolvedParamAddress = null;
|
|
4589
|
+
if (params?.address && ADDRESS_AWARE_TEMPLATES.has(template)) {
|
|
4590
|
+
const normalized = await normalizeAddressInput(params.address, {
|
|
4591
|
+
suiRpcUrl: context.suiRpcUrl,
|
|
4592
|
+
signal: context.signal
|
|
4593
|
+
});
|
|
4594
|
+
resolvedParamAddress = normalized.address;
|
|
4595
|
+
suinsName = normalized.suinsName;
|
|
4596
|
+
}
|
|
4425
4597
|
const resolveAddressTarget = () => {
|
|
4426
|
-
const fromParams =
|
|
4598
|
+
const fromParams = resolvedParamAddress;
|
|
4427
4599
|
const fromContext = context.walletAddress;
|
|
4428
4600
|
const target = fromParams ?? fromContext ?? null;
|
|
4429
4601
|
const isSelfRender = !!target && !!fromContext && target.toLowerCase() === fromContext.toLowerCase();
|
|
4430
|
-
return { address: target, isSelfRender };
|
|
4602
|
+
return { address: target, isSelfRender, suinsName };
|
|
4431
4603
|
};
|
|
4604
|
+
const formatAddrLabel = (address, suins) => suins ?? `${address.slice(0, 6)}\u2026${address.slice(-4)}`;
|
|
4432
4605
|
if (template === "full_portfolio") {
|
|
4433
|
-
const { address, isSelfRender } = resolveAddressTarget();
|
|
4606
|
+
const { address, isSelfRender, suinsName: resolvedSuins } = resolveAddressTarget();
|
|
4434
4607
|
if (!address) {
|
|
4435
4608
|
return {
|
|
4436
4609
|
data: {
|
|
@@ -4442,7 +4615,8 @@ Always prefer the canvas for visualisation requests. After rendering, offer to e
|
|
|
4442
4615
|
displayText: "Full Portfolio requires an address."
|
|
4443
4616
|
};
|
|
4444
4617
|
}
|
|
4445
|
-
const
|
|
4618
|
+
const addrLabel = formatAddrLabel(address, resolvedSuins);
|
|
4619
|
+
const titleSuffix = isSelfRender ? "" : ` \u2014 ${addrLabel}`;
|
|
4446
4620
|
const pos = isSelfRender ? context.serverPositions : null;
|
|
4447
4621
|
const rate = normalizeSavingsRate(pos?.savingsRate);
|
|
4448
4622
|
const savings = pos?.savings ?? 0;
|
|
@@ -4456,40 +4630,42 @@ Always prefer the canvas for visualisation requests. After rendering, offer to e
|
|
|
4456
4630
|
available: true,
|
|
4457
4631
|
address,
|
|
4458
4632
|
isSelfRender,
|
|
4633
|
+
suinsName: resolvedSuins,
|
|
4459
4634
|
currentSavings: savings,
|
|
4460
4635
|
currentDebt: borrows,
|
|
4461
4636
|
healthFactor: pos?.healthFactor ?? null,
|
|
4462
4637
|
savingsRate: rate
|
|
4463
4638
|
}
|
|
4464
4639
|
},
|
|
4465
|
-
displayText: isSelfRender ? `Opened Full Portfolio Overview.` : `Opened Full Portfolio Overview for ${
|
|
4640
|
+
displayText: isSelfRender ? `Opened Full Portfolio Overview.` : `Opened Full Portfolio Overview for ${addrLabel}.`
|
|
4466
4641
|
};
|
|
4467
4642
|
}
|
|
4468
4643
|
if (template === "watch_address") {
|
|
4469
|
-
const targetAddress =
|
|
4470
|
-
if (!targetAddress
|
|
4644
|
+
const targetAddress = resolvedParamAddress ?? "";
|
|
4645
|
+
if (!targetAddress) {
|
|
4471
4646
|
return {
|
|
4472
4647
|
data: {
|
|
4473
4648
|
__canvas: true,
|
|
4474
4649
|
template,
|
|
4475
4650
|
title,
|
|
4476
|
-
templateData: { available: false, message: "Please provide a valid Sui address to watch." }
|
|
4651
|
+
templateData: { available: false, message: "Please provide a valid Sui address or SuiNS name to watch." }
|
|
4477
4652
|
},
|
|
4478
|
-
displayText: "No valid address provided. Ask the user for a Sui address."
|
|
4653
|
+
displayText: "No valid address provided. Ask the user for a Sui address or SuiNS name."
|
|
4479
4654
|
};
|
|
4480
4655
|
}
|
|
4656
|
+
const addrLabel = formatAddrLabel(targetAddress, suinsName);
|
|
4481
4657
|
return {
|
|
4482
4658
|
data: {
|
|
4483
4659
|
__canvas: true,
|
|
4484
4660
|
template,
|
|
4485
|
-
title: `Watch ${
|
|
4486
|
-
templateData: { available: true, address: targetAddress }
|
|
4661
|
+
title: `Watch ${addrLabel}`,
|
|
4662
|
+
templateData: { available: true, address: targetAddress, suinsName }
|
|
4487
4663
|
},
|
|
4488
|
-
displayText: `Opened Watch Address canvas for ${
|
|
4664
|
+
displayText: `Opened Watch Address canvas for ${addrLabel}.`
|
|
4489
4665
|
};
|
|
4490
4666
|
}
|
|
4491
4667
|
if (template === "portfolio_timeline") {
|
|
4492
|
-
const { address, isSelfRender } = resolveAddressTarget();
|
|
4668
|
+
const { address, isSelfRender, suinsName: resolvedSuins } = resolveAddressTarget();
|
|
4493
4669
|
if (!address) {
|
|
4494
4670
|
return {
|
|
4495
4671
|
data: {
|
|
@@ -4501,7 +4677,8 @@ Always prefer the canvas for visualisation requests. After rendering, offer to e
|
|
|
4501
4677
|
displayText: "Portfolio Timeline requires an address."
|
|
4502
4678
|
};
|
|
4503
4679
|
}
|
|
4504
|
-
const
|
|
4680
|
+
const addrLabel = formatAddrLabel(address, resolvedSuins);
|
|
4681
|
+
const titleSuffix = isSelfRender ? "" : ` \u2014 ${addrLabel}`;
|
|
4505
4682
|
return {
|
|
4506
4683
|
data: {
|
|
4507
4684
|
__canvas: true,
|
|
@@ -4510,14 +4687,15 @@ Always prefer the canvas for visualisation requests. After rendering, offer to e
|
|
|
4510
4687
|
templateData: {
|
|
4511
4688
|
available: true,
|
|
4512
4689
|
address,
|
|
4513
|
-
isSelfRender
|
|
4690
|
+
isSelfRender,
|
|
4691
|
+
suinsName: resolvedSuins
|
|
4514
4692
|
}
|
|
4515
4693
|
},
|
|
4516
|
-
displayText: isSelfRender ? `Opened Portfolio Timeline. Shows your net worth, savings, and debt over time.` : `Opened Portfolio Timeline for ${
|
|
4694
|
+
displayText: isSelfRender ? `Opened Portfolio Timeline. Shows your net worth, savings, and debt over time.` : `Opened Portfolio Timeline for ${addrLabel}.`
|
|
4517
4695
|
};
|
|
4518
4696
|
}
|
|
4519
4697
|
if (template === "spending_breakdown") {
|
|
4520
|
-
const { address, isSelfRender } = resolveAddressTarget();
|
|
4698
|
+
const { address, isSelfRender, suinsName: resolvedSuins } = resolveAddressTarget();
|
|
4521
4699
|
if (!address) {
|
|
4522
4700
|
return {
|
|
4523
4701
|
data: {
|
|
@@ -4529,7 +4707,8 @@ Always prefer the canvas for visualisation requests. After rendering, offer to e
|
|
|
4529
4707
|
displayText: "Spending Breakdown requires an address."
|
|
4530
4708
|
};
|
|
4531
4709
|
}
|
|
4532
|
-
const
|
|
4710
|
+
const addrLabel = formatAddrLabel(address, resolvedSuins);
|
|
4711
|
+
const titleSuffix = isSelfRender ? "" : ` \u2014 ${addrLabel}`;
|
|
4533
4712
|
return {
|
|
4534
4713
|
data: {
|
|
4535
4714
|
__canvas: true,
|
|
@@ -4538,14 +4717,15 @@ Always prefer the canvas for visualisation requests. After rendering, offer to e
|
|
|
4538
4717
|
templateData: {
|
|
4539
4718
|
available: true,
|
|
4540
4719
|
address,
|
|
4541
|
-
isSelfRender
|
|
4720
|
+
isSelfRender,
|
|
4721
|
+
suinsName: resolvedSuins
|
|
4542
4722
|
}
|
|
4543
4723
|
},
|
|
4544
|
-
displayText: isSelfRender ? `Opened Spending Breakdown. Shows your service spending by category.` : `Opened Spending Breakdown for ${
|
|
4724
|
+
displayText: isSelfRender ? `Opened Spending Breakdown. Shows your service spending by category.` : `Opened Spending Breakdown for ${addrLabel}.`
|
|
4545
4725
|
};
|
|
4546
4726
|
}
|
|
4547
4727
|
if (template === "activity_heatmap") {
|
|
4548
|
-
const { address, isSelfRender } = resolveAddressTarget();
|
|
4728
|
+
const { address, isSelfRender, suinsName: resolvedSuins } = resolveAddressTarget();
|
|
4549
4729
|
if (!address) {
|
|
4550
4730
|
return {
|
|
4551
4731
|
data: {
|
|
@@ -4557,7 +4737,8 @@ Always prefer the canvas for visualisation requests. After rendering, offer to e
|
|
|
4557
4737
|
displayText: "Activity Heatmap requires an address."
|
|
4558
4738
|
};
|
|
4559
4739
|
}
|
|
4560
|
-
const
|
|
4740
|
+
const addrLabel = formatAddrLabel(address, resolvedSuins);
|
|
4741
|
+
const titleSuffix = isSelfRender ? "" : ` \u2014 ${addrLabel}`;
|
|
4561
4742
|
return {
|
|
4562
4743
|
data: {
|
|
4563
4744
|
__canvas: true,
|
|
@@ -4566,10 +4747,11 @@ Always prefer the canvas for visualisation requests. After rendering, offer to e
|
|
|
4566
4747
|
templateData: {
|
|
4567
4748
|
available: true,
|
|
4568
4749
|
address,
|
|
4569
|
-
isSelfRender
|
|
4750
|
+
isSelfRender,
|
|
4751
|
+
suinsName: resolvedSuins
|
|
4570
4752
|
}
|
|
4571
4753
|
},
|
|
4572
|
-
displayText: isSelfRender ? `Opened Activity Heatmap for your wallet. Click any day to explore transactions.` : `Opened Activity Heatmap for ${
|
|
4754
|
+
displayText: isSelfRender ? `Opened Activity Heatmap for your wallet. Click any day to explore transactions.` : `Opened Activity Heatmap for ${addrLabel}. Click any day to explore that address's transactions.`
|
|
4573
4755
|
};
|
|
4574
4756
|
}
|
|
4575
4757
|
const positions = context.serverPositions;
|
|
@@ -4593,13 +4775,13 @@ Always prefer the canvas for visualisation requests. After rendering, offer to e
|
|
|
4593
4775
|
};
|
|
4594
4776
|
}
|
|
4595
4777
|
if (template === "health_simulator") {
|
|
4596
|
-
const { address: targetAddress, isSelfRender } = resolveAddressTarget();
|
|
4778
|
+
const { address: targetAddress, isSelfRender, suinsName: resolvedSuins } = resolveAddressTarget();
|
|
4597
4779
|
const seedFromPos = isSelfRender;
|
|
4598
4780
|
const seedSavings = seedFromPos ? totalSavings : 0;
|
|
4599
4781
|
const seedBorrows = seedFromPos ? totalBorrows : 0;
|
|
4600
4782
|
const seedHf = seedFromPos ? healthFactor : null;
|
|
4601
4783
|
const roundedDebt = seedBorrows >= 1 ? Math.round(seedBorrows) : seedBorrows > 0 ? parseFloat(seedBorrows.toFixed(4)) : 0;
|
|
4602
|
-
const titleSuffix = !targetAddress || isSelfRender ? "" : ` \u2014 ${targetAddress
|
|
4784
|
+
const titleSuffix = !targetAddress || isSelfRender ? "" : ` \u2014 ${formatAddrLabel(targetAddress, resolvedSuins)}`;
|
|
4603
4785
|
return {
|
|
4604
4786
|
data: {
|
|
4605
4787
|
__canvas: true,
|
|
@@ -4609,6 +4791,7 @@ Always prefer the canvas for visualisation requests. After rendering, offer to e
|
|
|
4609
4791
|
available: true,
|
|
4610
4792
|
address: targetAddress ?? "",
|
|
4611
4793
|
isSelfRender,
|
|
4794
|
+
suinsName: resolvedSuins,
|
|
4612
4795
|
initialCollateral: seedSavings > 0 ? Math.round(seedSavings) : 1500,
|
|
4613
4796
|
initialDebt: roundedDebt > 0 ? roundedDebt : seedSavings > 0 ? 0 : 500,
|
|
4614
4797
|
currentHf: seedHf
|
|
@@ -4729,13 +4912,12 @@ var yieldSummaryTool = buildTool({
|
|
|
4729
4912
|
}
|
|
4730
4913
|
}
|
|
4731
4914
|
});
|
|
4732
|
-
var SUI_ADDRESS_REGEX5 = /^0x[a-fA-F0-9]{1,64}$/;
|
|
4733
4915
|
var activitySummaryTool = buildTool({
|
|
4734
4916
|
name: "activity_summary",
|
|
4735
|
-
description:
|
|
4917
|
+
description: 'Returns a categorised DeFi activity summary for the signed-in user OR any public Sui address or SuiNS name: transaction count, breakdown by action type (saves, sends, borrows, repayments, swaps, payments), total moved, net savings change, and yield earned. Use when the user asks about activity, transaction history summary, or what someone has done recently. Pass `address` as a 0x address OR a SuiNS name (e.g. "alex.sui") to inspect a contact / watched / public wallet; defaults to the signed-in user when omitted.',
|
|
4736
4918
|
inputSchema: z.object({
|
|
4737
4919
|
period: z.enum(["week", "month", "year", "all"]).optional().describe("Time period. Defaults to current month."),
|
|
4738
|
-
address: z.string().
|
|
4920
|
+
address: z.string().optional().describe("Sui address (0x\u2026) or SuiNS name (alex.sui). Defaults to the signed-in wallet when omitted.")
|
|
4739
4921
|
}),
|
|
4740
4922
|
jsonSchema: {
|
|
4741
4923
|
type: "object",
|
|
@@ -4743,8 +4925,7 @@ var activitySummaryTool = buildTool({
|
|
|
4743
4925
|
period: { type: "string", enum: ["week", "month", "year", "all"] },
|
|
4744
4926
|
address: {
|
|
4745
4927
|
type: "string",
|
|
4746
|
-
|
|
4747
|
-
description: "Sui address to inspect (defaults to the signed-in wallet)"
|
|
4928
|
+
description: "Sui address (0x\u2026) or SuiNS name (e.g. alex.sui). The engine resolves the name to an on-chain address before querying. Omit to default to the signed-in wallet."
|
|
4748
4929
|
}
|
|
4749
4930
|
}
|
|
4750
4931
|
},
|
|
@@ -4752,7 +4933,18 @@ var activitySummaryTool = buildTool({
|
|
|
4752
4933
|
async call(input, context) {
|
|
4753
4934
|
const period = input.period ?? "month";
|
|
4754
4935
|
const apiUrl = context.env?.AUDRIC_INTERNAL_API_URL;
|
|
4755
|
-
|
|
4936
|
+
let suinsName = null;
|
|
4937
|
+
let targetAddress;
|
|
4938
|
+
if (input.address) {
|
|
4939
|
+
const normalized = await normalizeAddressInput(input.address, {
|
|
4940
|
+
suiRpcUrl: context.suiRpcUrl,
|
|
4941
|
+
signal: context.signal
|
|
4942
|
+
});
|
|
4943
|
+
targetAddress = normalized.address;
|
|
4944
|
+
suinsName = normalized.suinsName;
|
|
4945
|
+
} else {
|
|
4946
|
+
targetAddress = context.walletAddress;
|
|
4947
|
+
}
|
|
4756
4948
|
const isSelfQuery = !!context.walletAddress && !!targetAddress && targetAddress.toLowerCase() === context.walletAddress.toLowerCase();
|
|
4757
4949
|
const empty = {
|
|
4758
4950
|
period,
|
|
@@ -4762,7 +4954,8 @@ var activitySummaryTool = buildTool({
|
|
|
4762
4954
|
netSavingsUsd: 0,
|
|
4763
4955
|
yieldEarnedUsd: 0,
|
|
4764
4956
|
address: targetAddress,
|
|
4765
|
-
isSelfQuery
|
|
4957
|
+
isSelfQuery,
|
|
4958
|
+
suinsName
|
|
4766
4959
|
};
|
|
4767
4960
|
if (!apiUrl || !targetAddress) {
|
|
4768
4961
|
return { data: empty, displayText: "Activity summary not available." };
|
|
@@ -4777,11 +4970,12 @@ var activitySummaryTool = buildTool({
|
|
|
4777
4970
|
return { data: empty, displayText: `Could not fetch activity data (HTTP ${res.status}).` };
|
|
4778
4971
|
}
|
|
4779
4972
|
const raw = await res.json();
|
|
4780
|
-
const data = { ...raw, address: targetAddress, isSelfQuery };
|
|
4973
|
+
const data = { ...raw, address: targetAddress, isSelfQuery, suinsName };
|
|
4781
4974
|
const sorted = [...data.byAction ?? []].sort((a, b) => b.count - a.count);
|
|
4782
4975
|
const top = sorted.slice(0, 3).map((a) => `${a.action} (${a.count})`).join(", ");
|
|
4783
4976
|
const periodLabel = data.period === "all" ? "all time" : `this ${data.period}`;
|
|
4784
|
-
const
|
|
4977
|
+
const subjectLabel = suinsName ?? `${targetAddress.slice(0, 6)}\u2026${targetAddress.slice(-4)}`;
|
|
4978
|
+
const subjectPrefix = isSelfQuery ? "" : `${subjectLabel} \u2014 `;
|
|
4785
4979
|
return {
|
|
4786
4980
|
data,
|
|
4787
4981
|
displayText: data.totalTransactions > 0 ? `${subjectPrefix}${data.totalTransactions} transactions ${periodLabel}. Top: ${top}. Total moved: $${data.totalMovedUsd.toFixed(2)}. Net savings: $${data.netSavingsUsd.toFixed(2)}.` : `${subjectPrefix}No activity recorded for ${periodLabel}.`
|
|
@@ -4791,6 +4985,67 @@ var activitySummaryTool = buildTool({
|
|
|
4791
4985
|
}
|
|
4792
4986
|
}
|
|
4793
4987
|
});
|
|
4988
|
+
var inputSchema5 = z.object({
|
|
4989
|
+
name: z.string().describe(
|
|
4990
|
+
'A SuiNS name ending in `.sui` (e.g. "alex.sui", "team.alex.sui"). The engine resolves the name on-chain via Sui JSON-RPC.'
|
|
4991
|
+
)
|
|
4992
|
+
});
|
|
4993
|
+
var resolveSuinsTool = buildTool({
|
|
4994
|
+
name: "resolve_suins",
|
|
4995
|
+
description: 'Resolve a SuiNS name (e.g. "alex.sui", "obehi.sui") to its on-chain Sui address. Use this whenever the user asks for the address of a `.sui` name, asks who owns a name, or wants to verify a SuiNS name is registered. Returns the 0x-prefixed 64-hex address, or `registered: false` when the name isn\'t registered. Never use `web_search` for this \u2014 web_search doesn\'t index SuiNS records, but this tool queries the canonical on-chain registry. NOTE: For lookup queries about money flows ("what\'s alex.sui\'s balance / portfolio / health / transactions"), call the relevant read tool directly with `address: "alex.sui"` \u2014 those tools normalize SuiNS names internally, so an explicit `resolve_suins` round-trip is wasted.',
|
|
4996
|
+
inputSchema: inputSchema5,
|
|
4997
|
+
jsonSchema: {
|
|
4998
|
+
type: "object",
|
|
4999
|
+
properties: {
|
|
5000
|
+
name: {
|
|
5001
|
+
type: "string",
|
|
5002
|
+
description: 'A SuiNS name ending in .sui (e.g. "alex.sui"). The engine resolves it on-chain.'
|
|
5003
|
+
}
|
|
5004
|
+
},
|
|
5005
|
+
required: ["name"]
|
|
5006
|
+
},
|
|
5007
|
+
isReadOnly: true,
|
|
5008
|
+
// Names map to addresses on a per-block basis (registry can change).
|
|
5009
|
+
// Cheap, deterministic for a given block — safe to dedupe within a turn.
|
|
5010
|
+
cacheable: true,
|
|
5011
|
+
preflight: (input) => {
|
|
5012
|
+
const trimmed = input.name?.trim().toLowerCase();
|
|
5013
|
+
if (!trimmed) {
|
|
5014
|
+
return { valid: false, error: "name is required" };
|
|
5015
|
+
}
|
|
5016
|
+
if (!SUINS_NAME_REGEX.test(trimmed)) {
|
|
5017
|
+
return {
|
|
5018
|
+
valid: false,
|
|
5019
|
+
error: `"${input.name}" doesn't look like a SuiNS name. Names must end in .sui and use only lowercase letters, digits, and hyphens (e.g. alex.sui).`
|
|
5020
|
+
};
|
|
5021
|
+
}
|
|
5022
|
+
return { valid: true };
|
|
5023
|
+
},
|
|
5024
|
+
async call(input, context) {
|
|
5025
|
+
const name = input.name.trim().toLowerCase();
|
|
5026
|
+
let address;
|
|
5027
|
+
try {
|
|
5028
|
+
address = await resolveSuinsViaRpc(name, {
|
|
5029
|
+
suiRpcUrl: context.suiRpcUrl,
|
|
5030
|
+
signal: context.signal
|
|
5031
|
+
});
|
|
5032
|
+
} catch (err) {
|
|
5033
|
+
if (err instanceof SuinsRpcError) {
|
|
5034
|
+
throw err;
|
|
5035
|
+
}
|
|
5036
|
+
throw err;
|
|
5037
|
+
}
|
|
5038
|
+
const result = {
|
|
5039
|
+
name,
|
|
5040
|
+
address,
|
|
5041
|
+
registered: address !== null
|
|
5042
|
+
};
|
|
5043
|
+
return {
|
|
5044
|
+
data: result,
|
|
5045
|
+
displayText: address ? `${name} \u2192 \`${address.slice(0, 10)}\u2026${address.slice(-6)}\`` : `${name} is not a registered SuiNS name.`
|
|
5046
|
+
};
|
|
5047
|
+
}
|
|
5048
|
+
});
|
|
4794
5049
|
var tokenPricesTool = buildTool({
|
|
4795
5050
|
name: "token_prices",
|
|
4796
5051
|
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.',
|
|
@@ -4872,7 +5127,8 @@ var READ_TOOLS = [
|
|
|
4872
5127
|
createInvoiceTool,
|
|
4873
5128
|
spendingAnalyticsTool,
|
|
4874
5129
|
yieldSummaryTool,
|
|
4875
|
-
activitySummaryTool
|
|
5130
|
+
activitySummaryTool,
|
|
5131
|
+
resolveSuinsTool
|
|
4876
5132
|
];
|
|
4877
5133
|
var WRITE_TOOLS = [
|
|
4878
5134
|
saveDepositTool,
|
|
@@ -5274,7 +5530,7 @@ function guardCostWarning(tool, _call, conversationText) {
|
|
|
5274
5530
|
message: "This action has a monetary cost. Confirm the user is aware before proceeding."
|
|
5275
5531
|
};
|
|
5276
5532
|
}
|
|
5277
|
-
var
|
|
5533
|
+
var SUI_ADDRESS_REGEX2 = /^0x[a-fA-F0-9]{64}$/;
|
|
5278
5534
|
function normalizeAddress(addr) {
|
|
5279
5535
|
return addr.trim().toLowerCase();
|
|
5280
5536
|
}
|
|
@@ -5339,7 +5595,7 @@ function guardAddressSource(tool, call, userText, contacts, walletAddress) {
|
|
|
5339
5595
|
if (!rawTo) {
|
|
5340
5596
|
return { verdict: "pass", gate: "address_source", tier: "safety" };
|
|
5341
5597
|
}
|
|
5342
|
-
if (!
|
|
5598
|
+
if (!SUI_ADDRESS_REGEX2.test(rawTo)) {
|
|
5343
5599
|
return { verdict: "pass", gate: "address_source", tier: "safety" };
|
|
5344
5600
|
}
|
|
5345
5601
|
const normalizedTo = normalizeAddress(rawTo);
|
|
@@ -8268,6 +8524,6 @@ function sanitizeAnthropicMessages(messages) {
|
|
|
8268
8524
|
return merged;
|
|
8269
8525
|
}
|
|
8270
8526
|
|
|
8271
|
-
export { AnthropicProvider, BalanceTracker, CANVAS_TEMPLATES, ContextBudget, CostTracker, DEFAULT_GUARD_CONFIG, DEFAULT_LEASE_SEC, DEFAULT_PERMISSION_CONFIG, DEFAULT_POLL_BUDGET_MS, DEFAULT_POLL_INTERVAL_MS, DEFAULT_SYSTEM_PROMPT, EarlyToolDispatcher, InMemoryDefiCacheStore, InMemoryFetchLock, InMemoryNaviCacheStore, InMemoryWalletCacheStore, McpClientManager, McpResponseCache, MemorySessionStore, NAVI_ADDR_TTL_SEC, NAVI_MCP_CONFIG, NAVI_MCP_URL, NAVI_RATES_TTL_SEC, NAVI_SERVER_NAME, NaviTools, PERMISSION_PRESETS, QueryEngine, READ_TOOLS, RecipeRegistry, RetryTracker, TOOL_FLAGS, TOOL_MODIFIABLE_FIELDS, TxMutex, WRITE_TOOLS, _resetNaviCircuitBreaker, activitySummaryTool, adaptAllMcpTools, adaptAllServerTools, adaptMcpTool, applyToolFlags, awaitOrFetch, balanceCheckTool, borrowTool, budgetToolResult, buildCachedSystemPrompt, buildMcpTools, buildProactivenessInstructions, buildProfileContext, buildSelfEvaluationInstruction, buildStateContext, buildTool, claimRewardsTool, classifyEffort, clearPortfolioCache, clearPortfolioCacheFor, clearPriceMapCache, compactMessages, createGuardRunnerState, engineToSSE, estimateTokens, explainTxTool, extractConversationText, extractMcpText, fetchAddressDefiPortfolio, fetchAddressPortfolio, fetchAudricHistory, fetchAudricPortfolio, fetchAvailableRewards, fetchBalance, fetchHealthFactor, fetchPositions, fetchProtocolStats, fetchRates, fetchSavings, fetchTokenPrices, fetchWalletCoins, findTool, getAudricApiBase, getDefaultTools, getDefiCacheStore, getFetchLock, getMcpManager, getModifiableFields, getNaviCacheStore, getTelemetrySink, getToolFlags, getWalletAddress, getWalletCacheStore, guardArtifactPreview, guardStaleData, hasNaviMcp, healthCheckTool, loadRecipes, microcompact, mppServicesTool, naviKey, parseMcpJson, parseRecipe, parseSSE, payApiTool, portfolioAnalysisTool, protocolDeepDiveTool, ratesInfoTool, registerEngineTools, renderCanvasTool, repayDebtTool, requireAgent, resetDefiCacheStore, resetFetchLock, resetNaviCacheStore, resetTelemetrySink, resetWalletCacheStore, resolvePermissionTier, resolveUsdValue, runGuards, runTools, saveContactTool, saveDepositTool, savingsInfoTool, sendTransferTool, serializeSSE, setDefiCacheStore, setFetchLock, setNaviCacheStore, setTelemetrySink, setWalletCacheStore, spendingAnalyticsTool, swapExecuteTool, swapQuoteTool, tokenPricesTool, toolNameToOperation, toolsToDefinitions, transactionHistoryTool, transformBalance, transformHealthFactor, transformPositions, transformRates, transformRewards, transformSavings, updateGuardStateAfterToolResult, validateHistory, voloStakeTool, voloStatsTool, voloUnstakeTool, webSearchTool, withdrawTool, yieldSummaryTool };
|
|
8527
|
+
export { AnthropicProvider, BalanceTracker, CANVAS_TEMPLATES, ContextBudget, CostTracker, DEFAULT_GUARD_CONFIG, DEFAULT_LEASE_SEC, DEFAULT_PERMISSION_CONFIG, DEFAULT_POLL_BUDGET_MS, DEFAULT_POLL_INTERVAL_MS, DEFAULT_SYSTEM_PROMPT, EarlyToolDispatcher, InMemoryDefiCacheStore, InMemoryFetchLock, InMemoryNaviCacheStore, InMemoryWalletCacheStore, InvalidAddressError, McpClientManager, McpResponseCache, MemorySessionStore, NAVI_ADDR_TTL_SEC, NAVI_MCP_CONFIG, NAVI_MCP_URL, NAVI_RATES_TTL_SEC, NAVI_SERVER_NAME, NaviTools, PERMISSION_PRESETS, QueryEngine, READ_TOOLS, RecipeRegistry, RetryTracker, SUINS_NAME_REGEX, SUI_ADDRESS_REGEX, SUI_ADDRESS_STRICT_REGEX, SuinsNotRegisteredError, SuinsRpcError, TOOL_FLAGS, TOOL_MODIFIABLE_FIELDS, TxMutex, WRITE_TOOLS, _resetNaviCircuitBreaker, activitySummaryTool, adaptAllMcpTools, adaptAllServerTools, adaptMcpTool, applyToolFlags, awaitOrFetch, balanceCheckTool, borrowTool, budgetToolResult, buildCachedSystemPrompt, buildMcpTools, buildProactivenessInstructions, buildProfileContext, buildSelfEvaluationInstruction, buildStateContext, buildTool, claimRewardsTool, classifyEffort, clearPortfolioCache, clearPortfolioCacheFor, clearPriceMapCache, compactMessages, createGuardRunnerState, engineToSSE, estimateTokens, explainTxTool, extractConversationText, extractMcpText, fetchAddressDefiPortfolio, fetchAddressPortfolio, fetchAudricHistory, fetchAudricPortfolio, fetchAvailableRewards, fetchBalance, fetchHealthFactor, fetchPositions, fetchProtocolStats, fetchRates, fetchSavings, fetchTokenPrices, fetchWalletCoins, findTool, getAudricApiBase, getDefaultTools, getDefiCacheStore, getFetchLock, getMcpManager, getModifiableFields, getNaviCacheStore, getTelemetrySink, getToolFlags, getWalletAddress, getWalletCacheStore, guardArtifactPreview, guardStaleData, hasNaviMcp, healthCheckTool, loadRecipes, looksLikeSuiNs, microcompact, mppServicesTool, naviKey, normalizeAddressInput, parseMcpJson, parseRecipe, parseSSE, payApiTool, portfolioAnalysisTool, protocolDeepDiveTool, ratesInfoTool, registerEngineTools, renderCanvasTool, repayDebtTool, requireAgent, resetDefiCacheStore, resetFetchLock, resetNaviCacheStore, resetTelemetrySink, resetWalletCacheStore, resolvePermissionTier, resolveSuinsTool, resolveSuinsViaRpc, resolveUsdValue, runGuards, runTools, saveContactTool, saveDepositTool, savingsInfoTool, sendTransferTool, serializeSSE, setDefiCacheStore, setFetchLock, setNaviCacheStore, setTelemetrySink, setWalletCacheStore, spendingAnalyticsTool, swapExecuteTool, swapQuoteTool, tokenPricesTool, toolNameToOperation, toolsToDefinitions, transactionHistoryTool, transformBalance, transformHealthFactor, transformPositions, transformRates, transformRewards, transformSavings, updateGuardStateAfterToolResult, validateHistory, voloStakeTool, voloStatsTool, voloUnstakeTool, webSearchTool, withdrawTool, yieldSummaryTool };
|
|
8272
8528
|
//# sourceMappingURL=index.js.map
|
|
8273
8529
|
//# sourceMappingURL=index.js.map
|