@t2000/engine 0.46.3 → 0.46.5
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 +11 -2
- package/dist/index.js +147 -54
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -1598,13 +1598,17 @@ interface AnthropicProviderConfig {
|
|
|
1598
1598
|
apiKey: string;
|
|
1599
1599
|
defaultModel?: string;
|
|
1600
1600
|
defaultMaxTokens?: number;
|
|
1601
|
+
/** Max retry attempts for retriable errors (overloaded, rate-limited, network). Default 3. */
|
|
1602
|
+
maxRetries?: number;
|
|
1601
1603
|
}
|
|
1602
1604
|
declare class AnthropicProvider implements LLMProvider {
|
|
1603
1605
|
private client;
|
|
1604
1606
|
private defaultModel;
|
|
1605
1607
|
private defaultMaxTokens;
|
|
1608
|
+
private maxRetries;
|
|
1606
1609
|
constructor(config: AnthropicProviderConfig);
|
|
1607
1610
|
chat(params: ChatParams): AsyncGenerator<ProviderEvent>;
|
|
1611
|
+
private streamOnce;
|
|
1608
1612
|
}
|
|
1609
1613
|
|
|
1610
1614
|
declare const CANVAS_TEMPLATES: readonly ["activity_heatmap", "portfolio_timeline", "yield_projector", "health_simulator", "dca_planner", "spending_breakdown", "watch_address", "full_portfolio"];
|
|
@@ -2051,11 +2055,16 @@ declare function fetchWalletCoins(address: string, rpcUrl?: string): Promise<Wal
|
|
|
2051
2055
|
/**
|
|
2052
2056
|
* Batch-fetch USD prices for Sui coin types from DefiLlama.
|
|
2053
2057
|
* Returns a map of `coinType -> usdPrice`. Tokens not found return no entry.
|
|
2054
|
-
*
|
|
2058
|
+
*
|
|
2059
|
+
* Cache behavior (post-0.47): merge-on-miss. If the cache is valid but missing
|
|
2060
|
+
* a few requested coin types, we fetch ONLY the missing ones and merge them
|
|
2061
|
+
* into the cached map — instead of throwing the entire cache away. This is
|
|
2062
|
+
* the common path when a wallet adds one new memecoin to a portfolio that
|
|
2063
|
+
* already has USDC/SUI prices cached. Saves ~0.5–1.5s on warm calls.
|
|
2055
2064
|
*/
|
|
2056
2065
|
declare function fetchTokenPrices(coinTypes: string[]): Promise<Record<string, number>>;
|
|
2057
2066
|
declare function clearPriceCache(): void;
|
|
2058
2067
|
|
|
2059
|
-
declare const 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 40 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.\n\n## Response rules\n- 1-2 sentences max. No bullet lists unless asked. No preambles.\n- Never say \"Would you like me to...\", \"Sure!\", \"Great question!\", \"Absolutely!\" \u2014 just do it or say you can't.\n- Lead with the result. After tool calls, state the outcome with real numbers. Done.\n- Present amounts as $1,234.56 and rates as X.XX% APY.\n- Show top 3 results unless asked for more. Summarize totals in one line.\n\n## Execution rule\nOnly offer to execute actions you have tools for. If you retrieved a quote, data, or information but have no tool to act on it, give the user the result and tell them where to execute manually \u2014 in one sentence. Never say \"Would you like me to proceed?\" unless you have a tool that can actually proceed.\n\n## Before acting\n- ALWAYS call a read tool first before any write tool \u2014 balance_check before save/send/borrow, savings_info before withdraw.\n- Show real numbers from tools \u2014 never fabricate rates, amounts, or balances.\n- When user says \"all\" or an imprecise amount, call the read tool first to get the exact number.\n\n## Tool usage\n- Use tools proactively \u2014 don't refuse requests you can handle.\n- For real-world questions (weather, search, news, prices), use pay_api. Tell the user the cost first.\n- For broad market data (yields across protocols, token prices, TVL, protocol comparisons), use defillama_* tools.\n- To discover Sui protocols, use defillama_sui_protocols first, then defillama_protocol_info with the slug.\n- Run multiple read-only tools in parallel when you need several data points.\n- If a tool errors, say what went wrong and what to try instead. One sentence.\n\n## Savings = USDC only (critical)\n- save_deposit accepts ONLY USDC. No other token can be deposited into savings.\n- When asked \"how much can I save?\", report only the user's USDC wallet balance (saveableUsdc field from balance_check). Other tokens like GOLD, SUI, USDT are NOT saveable and NOT savings positions \u2014 they are just wallet holdings.\n- NEVER say a non-USDC token is \"in savings\" or \"earning APY in savings\" unless it appears in the savings_info positions list. Wallet holdings \u2260 savings.\n- If user wants to save non-USDC tokens, tell them to swap to USDC first. Do NOT auto-chain swap + deposit.\n\n## Multi-step flows\n- \"How much X for Y?\": swap_quote first, then swap_execute if user confirms.\n- \"Swap then save\": swap_execute \u2192 balance_check \u2192 save_deposit. Confirm each step.\n- \"Buy $X of token\": defillama_token_prices \u2192 calculate amount \u2192 swap_execute.\n- \"Best yield on SUI\": compare rates_info (NAVI lending) + defillama_yield_pools (broader) + volo_stats.\n- withdraw supports legacy positions: USDC, USDe, USDsui, SUI. Pass asset param to withdraw a specific token.\n- \"Deposit SUI to earn yield\": volo_stake for SUI liquid staking. save_deposit is USDC only.\n- \"What protocols are on Sui?\": defillama_sui_protocols \u2192 defillama_protocol_info for details.\n- \"Full account report\" / \"
|
|
2068
|
+
declare const 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 40 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.\n\n## Response rules\n- 1-2 sentences max. No bullet lists unless asked. No preambles.\n- Never say \"Would you like me to...\", \"Sure!\", \"Great question!\", \"Absolutely!\" \u2014 just do it or say you can't.\n- Lead with the result. After tool calls, state the outcome with real numbers. Done.\n- Present amounts as $1,234.56 and rates as X.XX% APY.\n- Show top 3 results unless asked for more. Summarize totals in one line.\n\n## Execution rule\nOnly offer to execute actions you have tools for. If you retrieved a quote, data, or information but have no tool to act on it, give the user the result and tell them where to execute manually \u2014 in one sentence. Never say \"Would you like me to proceed?\" unless you have a tool that can actually proceed.\n\n## Before acting\n- ALWAYS call a read tool first before any write tool \u2014 balance_check before save/send/borrow, savings_info before withdraw.\n- Show real numbers from tools \u2014 never fabricate rates, amounts, or balances.\n- When user says \"all\" or an imprecise amount, call the read tool first to get the exact number.\n\n## Tool usage\n- Use tools proactively \u2014 don't refuse requests you can handle.\n- For real-world questions (weather, search, news, prices), use pay_api. Tell the user the cost first.\n- For broad market data (yields across protocols, token prices, TVL, protocol comparisons), use defillama_* tools.\n- To discover Sui protocols, use defillama_sui_protocols first, then defillama_protocol_info with the slug.\n- Run multiple read-only tools in parallel when you need several data points.\n- If a tool errors, say what went wrong and what to try instead. One sentence.\n\n## Savings = USDC only (critical)\n- save_deposit accepts ONLY USDC. No other token can be deposited into savings.\n- When asked \"how much can I save?\", report only the user's USDC wallet balance (saveableUsdc field from balance_check). Other tokens like GOLD, SUI, USDT are NOT saveable and NOT savings positions \u2014 they are just wallet holdings.\n- NEVER say a non-USDC token is \"in savings\" or \"earning APY in savings\" unless it appears in the savings_info positions list. Wallet holdings \u2260 savings.\n- If user wants to save non-USDC tokens, tell them to swap to USDC first. Do NOT auto-chain swap + deposit.\n\n## Multi-step flows\n- \"How much X for Y?\": swap_quote first, then swap_execute if user confirms.\n- \"Swap then save\": swap_execute \u2192 balance_check \u2192 save_deposit. Confirm each step.\n- \"Buy $X of token\": defillama_token_prices \u2192 calculate amount \u2192 swap_execute.\n- \"Best yield on SUI\": compare rates_info (NAVI lending) + defillama_yield_pools (broader) + volo_stats.\n- withdraw supports legacy positions: USDC, USDe, USDsui, SUI. Pass asset param to withdraw a specific token.\n- \"Deposit SUI to earn yield\": volo_stake for SUI liquid staking. save_deposit is USDC only.\n- \"What protocols are on Sui?\": defillama_sui_protocols \u2192 defillama_protocol_info for details.\n- \"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.\n\n## Safety\n- Never encourage risky financial behavior.\n- Warn when health factor < 1.5.\n- All amounts in USDC unless stated otherwise.";
|
|
2060
2069
|
|
|
2061
2070
|
export { AnthropicProvider, type AnthropicProviderConfig, type BalancePrices, type BalanceResult, BalanceTracker, type BuildToolOptions, CANVAS_TEMPLATES, type CanvasTemplate, type ChatParams, type CompactOptions, type ContentBlock, ContextBudget, type ContextBudgetConfig, type ConversationState, type ConversationStateStore, type CostSnapshot, CostTracker, type CostTrackerConfig, DEFAULT_GUARD_CONFIG, DEFAULT_PERMISSION_CONFIG, DEFAULT_SYSTEM_PROMPT, EarlyToolDispatcher, type EngineConfig, type EngineEvent, type GuardCheckResult, type GuardConfig, type GuardEvent, type GuardInjection, type GuardResult, type GuardRunnerState, type GuardTier, type GuardVerdict, type HealthFactorResult, type LLMProvider, type McpCallResult, McpClientManager, McpResponseCache, type McpServerConfig, type McpServerConnection, type McpToolAdapterConfig, type McpToolDescriptor, MemorySessionStore, type Message, NAVI_MCP_CONFIG, NAVI_MCP_URL, NAVI_SERVER_NAME, type NaviRawCoin, type NaviRawHealthFactor, type NaviRawPool, type NaviRawPosition, type NaviRawPositionsResponse, type NaviRawProtocolStats, type NaviRawRewardsResponse, type NaviReadOptions, NaviTools, type OutputConfig, PERMISSION_PRESETS, type PendingAction, type PendingActionModifiableField, type PendingReward, type PendingToolCall, type PermissionLevel, type PermissionOperation, type PermissionResponse, type PermissionRule, type PositionEntry, type PreflightResult, type ProtocolStats, type ProviderEvent, QueryEngine, READ_TOOLS, type RatesResult, type Recipe, type RecipePrerequisite, RecipeRegistry, type RecipeStep, type RecipeStepOnError, RetryTracker, type SSEEvent, type SavingsResult, type ServerPositionData, type SessionData, type SessionStore, type StateType, type StopReason, type SuiCoinBalance, type SystemBlock, type SystemPrompt, TOOL_FLAGS, TOOL_MODIFIABLE_FIELDS, type ThinkingConfig, type ThinkingEffort, type Tool, type ToolChoice, type ToolContext, type ToolDefinition, type ToolFlags, type ToolJsonSchema, type ToolResult, TxMutex, type UserFinancialProfile, type UserPermissionConfig, WRITE_TOOLS, type WalletCoin, activitySummaryTool, adaptAllMcpTools, adaptAllServerTools, adaptMcpTool, applyToolFlags, balanceCheckTool, borrowTool, budgetToolResult, buildCachedSystemPrompt, buildMcpTools, buildProactivenessInstructions, buildProfileContext, buildSelfEvaluationInstruction, buildStateContext, buildTool, claimRewardsTool, classifyEffort, clearPriceCache, compactMessages, createGuardRunnerState, defillamaChainTvlTool, defillamaPriceChangeTool, defillamaProtocolFeesTool, defillamaProtocolInfoTool, defillamaSuiProtocolsTool, defillamaTokenPricesTool, defillamaYieldPoolsTool, engineToSSE, estimateTokens, explainTxTool, extractConversationText, extractMcpText, 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, toolNameToOperation, toolsToDefinitions, transactionHistoryTool, transformBalance, transformHealthFactor, transformPositions, transformRates, transformRewards, transformSavings, updateGuardStateAfterToolResult, validateHistory, voloStakeTool, voloStatsTool, voloUnstakeTool, webSearchTool, withdrawTool, yieldSummaryTool };
|
package/dist/index.js
CHANGED
|
@@ -499,22 +499,26 @@ function parseMcpJson(content) {
|
|
|
499
499
|
var DEFILLAMA_PRICES_URL = "https://coins.llama.fi/prices/current";
|
|
500
500
|
var CACHE_TTL = 6e4;
|
|
501
501
|
var cache = null;
|
|
502
|
-
var
|
|
502
|
+
var pendingFetches = /* @__PURE__ */ new Map();
|
|
503
503
|
async function fetchTokenPrices(coinTypes) {
|
|
504
504
|
if (coinTypes.length === 0) return {};
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
if (
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
505
|
+
const now = Date.now();
|
|
506
|
+
const cacheValid = cache !== null && now - cache.ts < CACHE_TTL;
|
|
507
|
+
const cachedPrices = cacheValid ? cache.prices : {};
|
|
508
|
+
const missing = coinTypes.filter((ct) => !(ct in cachedPrices));
|
|
509
|
+
if (missing.length === 0) return cachedPrices;
|
|
510
|
+
const sig = missing.slice().sort().join("|");
|
|
511
|
+
let inflight = pendingFetches.get(sig);
|
|
512
|
+
if (!inflight) {
|
|
513
|
+
inflight = doFetch(missing).finally(() => {
|
|
514
|
+
pendingFetches.delete(sig);
|
|
515
|
+
});
|
|
516
|
+
pendingFetches.set(sig, inflight);
|
|
517
517
|
}
|
|
518
|
+
const fresh = await inflight;
|
|
519
|
+
const merged = { ...cachedPrices, ...fresh };
|
|
520
|
+
cache = { prices: merged, ts: cacheValid ? cache.ts : now };
|
|
521
|
+
return merged;
|
|
518
522
|
}
|
|
519
523
|
async function doFetch(coinTypes) {
|
|
520
524
|
const coins = coinTypes.map((ct) => `sui:${ct}`).join(",");
|
|
@@ -565,17 +569,18 @@ var balanceCheckTool = buildTool({
|
|
|
565
569
|
if (hasNaviMcp(context)) {
|
|
566
570
|
const address = getWalletAddress(context);
|
|
567
571
|
const mgr = getMcpManager(context);
|
|
572
|
+
const hasPositionFetcher = !!(context.positionFetcher && context.walletAddress);
|
|
568
573
|
const [walletCoins, positions, rewards] = await Promise.all([
|
|
569
574
|
fetchWalletCoins(address, context.suiRpcUrl).catch((err) => {
|
|
570
575
|
console.warn("[balance_check] Sui RPC coin fetch failed, falling back to MCP:", err);
|
|
571
576
|
return null;
|
|
572
577
|
}),
|
|
573
|
-
callNavi(mgr, NaviTools.GET_POSITIONS, {
|
|
578
|
+
hasPositionFetcher ? Promise.resolve(null) : callNavi(mgr, NaviTools.GET_POSITIONS, {
|
|
574
579
|
address,
|
|
575
580
|
protocols: "navi",
|
|
576
581
|
format: "json"
|
|
577
582
|
}),
|
|
578
|
-
callNavi(mgr, NaviTools.GET_AVAILABLE_REWARDS, { address })
|
|
583
|
+
hasPositionFetcher ? Promise.resolve(null) : callNavi(mgr, NaviTools.GET_AVAILABLE_REWARDS, { address })
|
|
579
584
|
]);
|
|
580
585
|
let coins = walletCoins;
|
|
581
586
|
if (!coins || coins.length === 0) {
|
|
@@ -591,10 +596,16 @@ var balanceCheckTool = buildTool({
|
|
|
591
596
|
}
|
|
592
597
|
const VSUI_COIN_TYPE = "0x549e8b69270defbfafd4f94e17ec44cdbdd99820b33bda2278dea3b9a32d3f55::cert::CERT";
|
|
593
598
|
const coinTypes = coins.map((c) => c.coinType).filter(Boolean);
|
|
594
|
-
const prices = await
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
599
|
+
const [prices, serverPositions] = await Promise.all([
|
|
600
|
+
fetchTokenPrices(coinTypes).catch((err) => {
|
|
601
|
+
console.warn("[balance_check] DefiLlama price fetch failed:", err);
|
|
602
|
+
return {};
|
|
603
|
+
}),
|
|
604
|
+
hasPositionFetcher ? context.positionFetcher(context.walletAddress).catch((err) => {
|
|
605
|
+
console.warn("[balance_check] positionFetcher failed:", err);
|
|
606
|
+
return null;
|
|
607
|
+
}) : Promise.resolve(null)
|
|
608
|
+
]);
|
|
598
609
|
if (coins.some((c) => c.coinType === VSUI_COIN_TYPE) && !prices[VSUI_COIN_TYPE]) {
|
|
599
610
|
try {
|
|
600
611
|
const statsRes = await fetch("https://open-api.naviprotocol.io/api/volo/stats", {
|
|
@@ -642,11 +653,10 @@ var balanceCheckTool = buildTool({
|
|
|
642
653
|
let savings;
|
|
643
654
|
let debt;
|
|
644
655
|
let pendingRewardsUsd;
|
|
645
|
-
if (
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
pendingRewardsUsd = sp.pendingRewards;
|
|
656
|
+
if (serverPositions) {
|
|
657
|
+
savings = serverPositions.savings;
|
|
658
|
+
debt = serverPositions.borrows;
|
|
659
|
+
pendingRewardsUsd = serverPositions.pendingRewards;
|
|
650
660
|
} else {
|
|
651
661
|
const posEntries = transformPositions(positions);
|
|
652
662
|
const rewardEntries = transformRewards(rewards);
|
|
@@ -2062,7 +2072,18 @@ var portfolioAnalysisTool = buildTool({
|
|
|
2062
2072
|
}
|
|
2063
2073
|
const rpcUrl = context.suiRpcUrl ?? "https://fullnode.mainnet.sui.io:443";
|
|
2064
2074
|
const DUST_USD = 0.01;
|
|
2065
|
-
const
|
|
2075
|
+
const apiUrl = context.env?.AUDRIC_INTERNAL_API_URL;
|
|
2076
|
+
const [coins, positions, weekHistResult] = await Promise.all([
|
|
2077
|
+
fetchWalletCoins(address, rpcUrl),
|
|
2078
|
+
context.positionFetcher ? context.positionFetcher(address).catch((err) => {
|
|
2079
|
+
console.warn("[portfolio_analysis] positionFetcher failed:", err);
|
|
2080
|
+
return null;
|
|
2081
|
+
}) : Promise.resolve(null),
|
|
2082
|
+
apiUrl ? fetch(
|
|
2083
|
+
`${apiUrl}/api/analytics/portfolio-history?days=7`,
|
|
2084
|
+
{ headers: { "x-sui-address": address }, signal: context.signal }
|
|
2085
|
+
).then((res) => res.ok ? res.json() : null).catch(() => null) : Promise.resolve(null)
|
|
2086
|
+
]);
|
|
2066
2087
|
const nonZero = coins.filter((c) => Number(c.totalBalance) > 0);
|
|
2067
2088
|
const prices = await fetchTokenPrices(nonZero.map((c) => c.coinType)).catch(() => ({}));
|
|
2068
2089
|
let walletValue = 0;
|
|
@@ -2080,35 +2101,18 @@ var portfolioAnalysisTool = buildTool({
|
|
|
2080
2101
|
let healthFactor = null;
|
|
2081
2102
|
let savingsApy;
|
|
2082
2103
|
let dailyEarning;
|
|
2083
|
-
if (
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
savingsApy = positions.savingsRate;
|
|
2091
|
-
dailyEarning = savingsValue * savingsApy / 365;
|
|
2092
|
-
}
|
|
2093
|
-
} catch {
|
|
2104
|
+
if (positions) {
|
|
2105
|
+
savingsValue = positions.savings ?? 0;
|
|
2106
|
+
debtValue = positions.borrows ?? 0;
|
|
2107
|
+
healthFactor = positions.healthFactor ?? null;
|
|
2108
|
+
if (typeof positions.savingsRate === "number" && positions.savingsRate > 0) {
|
|
2109
|
+
savingsApy = positions.savingsRate;
|
|
2110
|
+
dailyEarning = savingsValue * savingsApy / 365;
|
|
2094
2111
|
}
|
|
2095
2112
|
}
|
|
2096
2113
|
let weekChange;
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
try {
|
|
2100
|
-
const histRes = await fetch(
|
|
2101
|
-
`${apiUrl}/api/analytics/portfolio-history?days=7`,
|
|
2102
|
-
{ headers: { "x-sui-address": address }, signal: context.signal }
|
|
2103
|
-
);
|
|
2104
|
-
if (histRes.ok) {
|
|
2105
|
-
const hist = await histRes.json();
|
|
2106
|
-
if (hist.change && hist.change.absoluteUsd !== 0) {
|
|
2107
|
-
weekChange = hist.change;
|
|
2108
|
-
}
|
|
2109
|
-
}
|
|
2110
|
-
} catch {
|
|
2111
|
-
}
|
|
2114
|
+
if (weekHistResult?.change && weekHistResult.change.absoluteUsd !== 0) {
|
|
2115
|
+
weekChange = weekHistResult.change;
|
|
2112
2116
|
}
|
|
2113
2117
|
const totalValue = walletValue + savingsValue;
|
|
2114
2118
|
for (const a of allocations) {
|
|
@@ -3333,7 +3337,7 @@ Only offer to execute actions you have tools for. If you retrieved a quote, data
|
|
|
3333
3337
|
- withdraw supports legacy positions: USDC, USDe, USDsui, SUI. Pass asset param to withdraw a specific token.
|
|
3334
3338
|
- "Deposit SUI to earn yield": volo_stake for SUI liquid staking. save_deposit is USDC only.
|
|
3335
3339
|
- "What protocols are on Sui?": defillama_sui_protocols \u2192 defillama_protocol_info for details.
|
|
3336
|
-
- "Full account report" / "give me everything" / "complete overview"
|
|
3340
|
+
- "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.
|
|
3337
3341
|
|
|
3338
3342
|
## Safety
|
|
3339
3343
|
- Never encourage risky financial behavior.
|
|
@@ -5302,8 +5306,9 @@ function classifyEffort(model, userMessage, matchedRecipe, sessionWriteCount) {
|
|
|
5302
5306
|
if (matchedRecipe && matchedRecipe.steps.length >= 3) return "high";
|
|
5303
5307
|
if (matchedRecipe?.name === "safe_borrow" || matchedRecipe?.name === "bulk_mail") return "high";
|
|
5304
5308
|
if (sessionWriteCount > 0 && /borrow|withdraw|send|swap/i.test(msg)) return "high";
|
|
5305
|
-
if (
|
|
5306
|
-
if (
|
|
5309
|
+
if (matchedRecipe) return "medium";
|
|
5310
|
+
if (/\b(balance|rate|how much|what is|check|price|apy|hf)\b/i.test(msg)) return "low";
|
|
5311
|
+
if (!/\b(deposit|send|swap|borrow|withdraw|save|pay|transfer|show|history|all|list|everything|report|summary|breakdown)\b/i.test(msg)) return "low";
|
|
5307
5312
|
return "medium";
|
|
5308
5313
|
}
|
|
5309
5314
|
|
|
@@ -5724,16 +5729,51 @@ function adaptAllServerTools(manager, serverConfigs) {
|
|
|
5724
5729
|
}
|
|
5725
5730
|
var DEFAULT_MODEL = "claude-sonnet-4-20250514";
|
|
5726
5731
|
var DEFAULT_MAX_TOKENS2 = 4096;
|
|
5732
|
+
var DEFAULT_MAX_RETRIES = 3;
|
|
5733
|
+
var RETRY_BASE_DELAY_MS = 1e3;
|
|
5734
|
+
var RETRY_MAX_DELAY_MS = 8e3;
|
|
5727
5735
|
var AnthropicProvider = class {
|
|
5728
5736
|
client;
|
|
5729
5737
|
defaultModel;
|
|
5730
5738
|
defaultMaxTokens;
|
|
5739
|
+
maxRetries;
|
|
5731
5740
|
constructor(config) {
|
|
5732
5741
|
this.client = new Anthropic({ apiKey: config.apiKey });
|
|
5733
5742
|
this.defaultModel = config.defaultModel ?? DEFAULT_MODEL;
|
|
5734
5743
|
this.defaultMaxTokens = config.defaultMaxTokens ?? DEFAULT_MAX_TOKENS2;
|
|
5744
|
+
this.maxRetries = config.maxRetries ?? DEFAULT_MAX_RETRIES;
|
|
5735
5745
|
}
|
|
5736
5746
|
async *chat(params) {
|
|
5747
|
+
let attempt = 0;
|
|
5748
|
+
while (true) {
|
|
5749
|
+
let yieldedAnything = false;
|
|
5750
|
+
const inner = this.streamOnce(params);
|
|
5751
|
+
try {
|
|
5752
|
+
for (; ; ) {
|
|
5753
|
+
const next = await inner.next();
|
|
5754
|
+
if (next.done) return;
|
|
5755
|
+
yieldedAnything = true;
|
|
5756
|
+
yield next.value;
|
|
5757
|
+
}
|
|
5758
|
+
} catch (err) {
|
|
5759
|
+
try {
|
|
5760
|
+
await inner.return?.(void 0);
|
|
5761
|
+
} catch {
|
|
5762
|
+
}
|
|
5763
|
+
if (!yieldedAnything && isRetriableError(err) && attempt < this.maxRetries) {
|
|
5764
|
+
attempt++;
|
|
5765
|
+
const delayMs = computeBackoffMs(attempt);
|
|
5766
|
+
console.warn(
|
|
5767
|
+
`[anthropic] retriable error (attempt ${attempt}/${this.maxRetries}, retrying in ${delayMs}ms): ${rawErrorMessage(err)}`
|
|
5768
|
+
);
|
|
5769
|
+
await sleep(delayMs);
|
|
5770
|
+
continue;
|
|
5771
|
+
}
|
|
5772
|
+
throw new Error(friendlyErrorMessage(err));
|
|
5773
|
+
}
|
|
5774
|
+
}
|
|
5775
|
+
}
|
|
5776
|
+
async *streamOnce(params) {
|
|
5737
5777
|
const messages = sanitizeAnthropicMessages(
|
|
5738
5778
|
params.messages.map(toAnthropicMessage)
|
|
5739
5779
|
);
|
|
@@ -5890,6 +5930,59 @@ var AnthropicProvider = class {
|
|
|
5890
5930
|
}
|
|
5891
5931
|
}
|
|
5892
5932
|
};
|
|
5933
|
+
function isRetriableError(err) {
|
|
5934
|
+
if (!err) return false;
|
|
5935
|
+
if (err instanceof Anthropic.APIError) {
|
|
5936
|
+
if (err.status === 529 || err.status === 408) return true;
|
|
5937
|
+
if (err.status === 502 || err.status === 503 || err.status === 504) return true;
|
|
5938
|
+
if (err.status === 429) return true;
|
|
5939
|
+
return false;
|
|
5940
|
+
}
|
|
5941
|
+
const msg = rawErrorMessage(err).toLowerCase();
|
|
5942
|
+
if (msg.includes("overloaded_error") || msg.includes('"overloaded"') || msg.includes("rate_limit_error") || msg.includes("econnreset") || msg.includes("etimedout") || msg.includes("socket hang up") || msg.includes("fetch failed") || msg.includes("network error")) {
|
|
5943
|
+
return true;
|
|
5944
|
+
}
|
|
5945
|
+
return false;
|
|
5946
|
+
}
|
|
5947
|
+
function rawErrorMessage(err) {
|
|
5948
|
+
if (err instanceof Error) return err.message;
|
|
5949
|
+
if (typeof err === "string") return err;
|
|
5950
|
+
try {
|
|
5951
|
+
return JSON.stringify(err);
|
|
5952
|
+
} catch {
|
|
5953
|
+
return String(err);
|
|
5954
|
+
}
|
|
5955
|
+
}
|
|
5956
|
+
function friendlyErrorMessage(err) {
|
|
5957
|
+
const msg = rawErrorMessage(err).toLowerCase();
|
|
5958
|
+
if (msg.includes("overloaded_error") || msg.includes('"overloaded"') || err instanceof Anthropic.APIError && err.status === 529) {
|
|
5959
|
+
return "Anthropic's servers are over capacity right now. Please try again in 30 seconds.";
|
|
5960
|
+
}
|
|
5961
|
+
if (msg.includes("rate_limit_error") || err instanceof Anthropic.APIError && err.status === 429) {
|
|
5962
|
+
return "Too many requests in a short window. Please wait a moment and try again.";
|
|
5963
|
+
}
|
|
5964
|
+
if (msg.includes("econnreset") || msg.includes("etimedout") || msg.includes("socket hang up") || msg.includes("fetch failed") || msg.includes("network error")) {
|
|
5965
|
+
return "Couldn't reach Anthropic. Check your connection and try again.";
|
|
5966
|
+
}
|
|
5967
|
+
if (err instanceof Anthropic.APIError && err.status === 401) {
|
|
5968
|
+
return "Authentication failed. Please check the Anthropic API key configuration.";
|
|
5969
|
+
}
|
|
5970
|
+
if (err instanceof Anthropic.APIError && err.status === 400) {
|
|
5971
|
+
return "The request was rejected by Anthropic. This is likely a bug \u2014 please retry, and if it persists, contact support.";
|
|
5972
|
+
}
|
|
5973
|
+
if (err instanceof Anthropic.APIError && err.status >= 500) {
|
|
5974
|
+
return "Anthropic returned a server error. Please try again in a moment.";
|
|
5975
|
+
}
|
|
5976
|
+
return "Something went wrong. Please try again.";
|
|
5977
|
+
}
|
|
5978
|
+
function computeBackoffMs(attempt) {
|
|
5979
|
+
const base = Math.min(RETRY_BASE_DELAY_MS * 2 ** (attempt - 1), RETRY_MAX_DELAY_MS);
|
|
5980
|
+
const jitter = Math.floor(Math.random() * 250);
|
|
5981
|
+
return base + jitter;
|
|
5982
|
+
}
|
|
5983
|
+
function sleep(ms) {
|
|
5984
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
5985
|
+
}
|
|
5893
5986
|
function toAnthropicSystem(prompt) {
|
|
5894
5987
|
if (typeof prompt === "string") return prompt;
|
|
5895
5988
|
return prompt.map((block) => ({
|