@t2000/engine 1.22.1 → 1.23.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 +7 -4
- package/dist/index.d.ts +8 -5
- package/dist/index.js +134 -24
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @t2000/engine
|
|
2
2
|
|
|
3
|
-
Agent engine for conversational finance — implements **Audric Intelligence** (the moat behind the Audric consumer product). Five systems work together: Agent Harness (
|
|
3
|
+
Agent engine for conversational finance — implements **Audric Intelligence** (the moat behind the Audric consumer product). Five systems work together: Agent Harness (35 tools — 24 read, 11 write), Reasoning Engine (14 guards across 3 priority tiers + 6 YAML skill recipes), Silent Profile, Chain Memory, and AdviceLog. Every action it triggers waits on Audric Passport's tap-to-confirm.
|
|
4
4
|
|
|
5
5
|
QueryEngine orchestrates LLM conversations, financial tools, user confirmations, and MCP integrations into a single async-generator loop.
|
|
6
6
|
|
|
@@ -35,11 +35,11 @@ for await (const event of engine.submitMessage('What is my balance?')) {
|
|
|
35
35
|
|
|
36
36
|
## Audric Intelligence — the 5 systems
|
|
37
37
|
|
|
38
|
-
> _Not a chatbot. A financial agent._ Five systems work together to **understand** the user's money, **reason** about decisions, **act** through
|
|
38
|
+
> _Not a chatbot. A financial agent._ Five systems work together to **understand** the user's money, **reason** about decisions, **act** through 35 financial tools in one conversation, **remember** what they did on-chain, and **remember what it told them**. Every action still waits on Audric Passport's tap-to-confirm.
|
|
39
39
|
|
|
40
40
|
| System | One-line | Owns | Lives in |
|
|
41
41
|
|---|---|---|---|
|
|
42
|
-
| 🎛️ **Agent Harness** |
|
|
42
|
+
| 🎛️ **Agent Harness** | 35 tools, one agent. | Tool registry, parallel reads, serial writes, permission gates, streaming dispatch | `engine.ts`, `tool.ts`, `orchestration.ts`, `tools/*` |
|
|
43
43
|
| ⚡ **Reasoning Engine** | Thinks before it acts. | Adaptive thinking effort, 14 guards (12 pre-exec + 2 post-exec), 6 YAML skill recipes, prompt caching, preflight validation | `classify-effort.ts`, `guards.ts`, `recipes/registry.ts`, `engine.ts` `cache_control` |
|
|
44
44
|
| 🧠 **Silent Profile** | Knows your finances. | Daily on-chain orientation snapshot + Claude-inferred profile, injected as `<financial_context>` block at every boot | _Audric-side_: `UserFinancialContext` + `UserFinancialProfile` Prisma models + `buildFinancialContextBlock()` |
|
|
45
45
|
| 🔗 **Chain Memory** | Remembers what you do on-chain. | 7 classifiers extract `ChainFact` rows from on-chain history, hydrated as silent context | _Audric-side_: 7 classifier crons + `ChainFact` Prisma model + `buildMemoryContext()` |
|
|
@@ -161,7 +161,10 @@ QueryEngine.submitMessage()
|
|
|
161
161
|
> `defillama_sui_protocols`. Added 1 — `token_prices` (BlockVision-backed). `balance_check`
|
|
162
162
|
> and `portfolio_analysis` rewired to BlockVision Indexer REST API for sub-500ms portfolio
|
|
163
163
|
> fetches. `protocol_deep_dive` retains its DefiLlama dependency (narrow scope, no
|
|
164
|
-
> equivalent on BlockVision). Net: 23 reads + 11 writes = 34 tools.
|
|
164
|
+
> equivalent on BlockVision). Net post-v1.4: 23 reads + 11 writes = 34 tools.
|
|
165
|
+
>
|
|
166
|
+
> **SPEC 10 SuiNS reverse-lookup (May 2026):** Added 1 read tool — `resolve_suins`.
|
|
167
|
+
> Net post-SPEC-10: **24 reads + 11 writes = 35 tools** (current).
|
|
165
168
|
|
|
166
169
|
## Recent Upgrades — Spec 1 (Correctness) + Spec 2 (Intelligence)
|
|
167
170
|
|
package/dist/index.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
|
|
3
3
|
import { Transport } from '@modelcontextprotocol/sdk/shared/transport.js';
|
|
4
4
|
import { Tool as Tool$1 } from '@modelcontextprotocol/sdk/types.js';
|
|
5
5
|
import * as _t2000_sdk from '@t2000/sdk';
|
|
6
|
-
import { T2000 } from '@t2000/sdk';
|
|
6
|
+
import { PendingReward as PendingReward$1, T2000 } from '@t2000/sdk';
|
|
7
7
|
|
|
8
8
|
type ProactiveType = 'idle_balance' | 'hf_warning' | 'apy_drift' | 'goal_progress';
|
|
9
9
|
interface ProactiveMarker {
|
|
@@ -3363,13 +3363,16 @@ declare const repayDebtTool: Tool<{
|
|
|
3363
3363
|
gasCost: number;
|
|
3364
3364
|
}>;
|
|
3365
3365
|
|
|
3366
|
-
|
|
3366
|
+
interface ClaimRewardsResult {
|
|
3367
3367
|
success: boolean;
|
|
3368
3368
|
tx: string | null;
|
|
3369
|
-
rewards:
|
|
3369
|
+
rewards: PendingReward$1[];
|
|
3370
3370
|
totalValueUsd: number;
|
|
3371
3371
|
gasCost: number;
|
|
3372
|
-
|
|
3372
|
+
degraded: boolean;
|
|
3373
|
+
degradationReason: string | null;
|
|
3374
|
+
}
|
|
3375
|
+
declare const claimRewardsTool: Tool<{}, ClaimRewardsResult>;
|
|
3373
3376
|
|
|
3374
3377
|
declare const payApiTool: Tool<{
|
|
3375
3378
|
url: string;
|
|
@@ -4203,7 +4206,7 @@ declare function fetchAudricHistory(address: string, opts: {
|
|
|
4203
4206
|
limit?: number;
|
|
4204
4207
|
}, env?: Record<string, string>, signal?: AbortSignal): Promise<AudricHistoryRecord[] | null>;
|
|
4205
4208
|
|
|
4206
|
-
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 34 tools, Reasoning Engine with 14 guards and 6 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- 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## Caption rules (after tool calls)\n- **When a canvas was rendered (`render_canvas` was called, or any tool that auto-renders a card like balance_check / portfolio_analysis / savings_info / health_check / transaction_history): the canvas IS the answer.** Your chat message must NOT restate wallet, savings, debt, holdings, or net-worth numbers \u2014 they are already on screen. Add at most ONE sentence of context, advice, or next step (e.g. \"Your USDC is idle \u2014 consider depositing for ~4.5% APY\"), or say nothing.\n- **When NO canvas was rendered:** lead with the result and quote the actual numbers from the tool. One sentence.\n- **NEVER describe a position as \"no\", \"none\", \"minimal\", \"zero\", or \"inactive\" if the tool result contains a positive value for that field.** The tool result is the source of truth \u2014 never your interior summary. If the canvas shows $100 in savings, you cannot say \"no active savings\" in the caption.\n- **NEVER claim \"no DeFi positions\" when the tool result says the DeFi slice is UNAVAILABLE.** When `balance_check` displayText contains \"DeFi positions: UNAVAILABLE\" or \"DeFi data source unreachable\", the slice is unknown \u2014 say \"DeFi data is currently unavailable\" or omit the mention. Only claim \"no DeFi positions\" when the displayText explicitly omits any DeFi line (i.e. the fetch succeeded with $0 across every covered protocol).\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 NAVI lending APYs, use rates_info; for VOLO liquid staking stats, use volo_stats; for spot token prices, use token_prices.\n- For protocol-level due diligence (TVL, fees, audits, safety) on Sui DeFi protocols, use protocol_deep_dive 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 or USDsui (critical)\n- save_deposit and borrow accept ONLY USDC or USDsui. No other token can be deposited or borrowed.\n- USDC is the canonical default. USDsui is permitted because it has a productive NAVI pool (often a higher APY than USDC). All other holdings (GOLD, SUI, USDT, USDe, ETH, NAVX, WAL) are NOT saveable.\n- When asked \"how much can I save?\":\n - Report saveableUsdc from balance_check (the user's USDC wallet balance \u2014 canonical saveable).\n - If the user also holds USDsui in their wallet, report that separately as \"USDsui (saveable): X.XX\". Do NOT roll the two together \u2014 the LLM must keep the per-asset distinction so the user can pick.\n- When the user says \"save 10 USDC\" \u2192 call save_deposit with asset=\"USDC\". When they say \"save 10 USDsui\" \u2192 call with asset=\"USDsui\". Never silently substitute.\n- When the user says \"save 10\" (no asset) \u2192 call balance_check first and ask which stable they want, OR pick whichever they hold more of with a one-line explanation.\n- \"Best stable to save right now?\" \u2192 call rates_info to compare USDC vs USDsui APY on NAVI; let the user pick.\n- NEVER say a non-saveable token (GOLD, SUI, USDT, etc.) is \"in savings\" or \"earning APY in savings\". Wallet holdings \u2260 savings positions, even for stables we don't accept.\n- If user wants to save a non-saveable token, tell them to swap to USDC or USDsui first. Do NOT auto-chain swap + deposit.\n- Repay symmetry: a USDsui debt MUST be repaid with USDsui (and USDC debt with USDC). When calling repay_debt, pass asset=\"USDsui\" if the borrow is USDsui. If the user asks \"repay my debt\" and savings_info shows borrows in BOTH stables, list both and ask which to repay first. If the user holds the wrong stable, tell them to swap manually \u2014 do NOT auto-chain swap + repay.\n\n## Fees (critical \u2014 never deny having fees)\n- **Swap:** 0.1% Audric overlay fee on the output amount, taken by the aggregator and sent to the Audric treasury. The Cetus DEX fee (typically 0.01\u20130.25%) is separate and goes to the DEX. Both are shown on the swap card. Never say Audric takes no cut on swaps \u2014 it does.\n- **Save (deposit):** 0.1% Audric fee on the deposit amount, taken atomically in the same transaction.\n- **Borrow:** 0.05% Audric fee on the borrow amount, taken atomically in the same transaction.\n- **Withdraw / Repay / Send / Receive:** No Audric fee. Gas is sponsored (free to the user).\n- When a user asks about fees, quote the above. Do NOT say \"I don't take a cut\", \"fees are zero\", \"all your value stays with you\", or \"I'm here to execute, not extract\" \u2014 those are incorrect for swap, save, and borrow.\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\": token_prices \u2192 calculate amount \u2192 swap_execute.\n- \"Best yield on SUI\": compare rates_info (NAVI lending) + volo_stats (vSUI liquid staking).\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 only accepts USDC or USDsui.\n- \"Is protocol X safe?\" / \"Tell me about NAVI\": protocol_deep_dive with the slug.\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.\n\n## Proactive insights (only when there's a clear opportunity)\n- When you spot a financial insight worth surfacing \u2014 idle balance worth saving, health factor approaching the warning band, APY drift on a known position, progress against a saved goal \u2014 emit a `<proactive type=\"...\" subjectKey=\"...\">BODY</proactive>` block. ALWAYS use the wrapper \u2014 plain-text proactive prose without the wrapper renders as regular text and skips the engine's per-session cooldown (the same nudge will then re-fire every turn).\n- Two valid placements \u2014 pick whichever fits the turn:\n - **No user question** (or the question is unrelated to the insight): wrap your ENTIRE response in the `<proactive>` block.\n - **You're answering a user question AND have a related insight to add**: answer the question normally, then APPEND the `<proactive>` block at the end, separated by a line break. The \"after the answer\" form is also taught in detail under \u00A7 Proactive Awareness \u2014 same syntax, both placements valid.\n- The host renders the wrapped block with a distinct \"\u2726 ADDED BY AUDRIC\" lockup so the user knows this is your suggestion, not an answer.\n- Allowed types (closed list \u2014 anything else is dropped): `idle_balance` (cash sitting idle that could earn yield), `hf_warning` (debt position approaching liquidation), `apy_drift` (rate change on a position they hold), `goal_progress` (update on a saved goal).\n- `subjectKey` is a stable identifier for the SPECIFIC subject \u2014 examples: `USDC` or `USDsui` for an idle-balance insight on either NAVI-saveable stable, `1.45` for a HF warning at that level, `save-500-by-may` for goal progress. Same (type, subjectKey) won't fire twice in one session \u2014 pick the same key for the same subject so the engine cooldown works.\n- Cap: at most ONE proactive block per turn.\n- Skip proactive blocks when nothing notable changed since the last turn, when the user is mid-flow on something else, or when you'd just be restating the financial-context block. Quality over quantity \u2014 a block ignored is worse than no block.";
|
|
4209
|
+
declare const DEFAULT_SYSTEM_PROMPT: string;
|
|
4207
4210
|
|
|
4208
4211
|
/** Cache entry shape. `cachedAt` is the wall-clock ms at write time. */
|
|
4209
4212
|
interface NaviCacheEntry {
|
package/dist/index.js
CHANGED
|
@@ -252,7 +252,7 @@ function isBundleableTool(name) {
|
|
|
252
252
|
return TOOL_FLAGS[name]?.bundleable === true;
|
|
253
253
|
}
|
|
254
254
|
|
|
255
|
-
// src/navi
|
|
255
|
+
// src/navi/config.ts
|
|
256
256
|
var NAVI_SERVER_NAME = "navi";
|
|
257
257
|
var NAVI_MCP_URL = "https://open-api.naviprotocol.io/api/mcp";
|
|
258
258
|
var NAVI_MCP_CONFIG = {
|
|
@@ -521,7 +521,7 @@ async function fetchWalletCoins(address, rpcUrl) {
|
|
|
521
521
|
});
|
|
522
522
|
}
|
|
523
523
|
|
|
524
|
-
// src/defi
|
|
524
|
+
// src/cache/defi.ts
|
|
525
525
|
var InMemoryDefiCacheStore = class {
|
|
526
526
|
store = /* @__PURE__ */ new Map();
|
|
527
527
|
async get(address) {
|
|
@@ -557,7 +557,7 @@ function resetDefiCacheStore() {
|
|
|
557
557
|
activeStore = new InMemoryDefiCacheStore();
|
|
558
558
|
}
|
|
559
559
|
|
|
560
|
-
// src/wallet
|
|
560
|
+
// src/cache/wallet.ts
|
|
561
561
|
var InMemoryWalletCacheStore = class {
|
|
562
562
|
store = /* @__PURE__ */ new Map();
|
|
563
563
|
async get(address) {
|
|
@@ -1115,6 +1115,28 @@ var DEFI_PROTOCOLS = [
|
|
|
1115
1115
|
"suistake",
|
|
1116
1116
|
"walrus"
|
|
1117
1117
|
];
|
|
1118
|
+
var DEFI_PROTOCOL_CONCURRENCY = 3;
|
|
1119
|
+
async function mapWithConcurrency(items, fn, concurrency) {
|
|
1120
|
+
if (items.length === 0) return [];
|
|
1121
|
+
const results = new Array(items.length);
|
|
1122
|
+
let nextIdx = 0;
|
|
1123
|
+
async function worker() {
|
|
1124
|
+
while (true) {
|
|
1125
|
+
const myIdx = nextIdx;
|
|
1126
|
+
nextIdx += 1;
|
|
1127
|
+
if (myIdx >= items.length) return;
|
|
1128
|
+
try {
|
|
1129
|
+
const value = await fn(items[myIdx], myIdx);
|
|
1130
|
+
results[myIdx] = { status: "fulfilled", value };
|
|
1131
|
+
} catch (reason) {
|
|
1132
|
+
results[myIdx] = { status: "rejected", reason };
|
|
1133
|
+
}
|
|
1134
|
+
}
|
|
1135
|
+
}
|
|
1136
|
+
const workerCount = Math.max(1, Math.min(concurrency, items.length));
|
|
1137
|
+
await Promise.all(Array.from({ length: workerCount }, () => worker()));
|
|
1138
|
+
return results;
|
|
1139
|
+
}
|
|
1118
1140
|
var SUI_TYPE_FULL = "0x0000000000000000000000000000000000000000000000000000000000000002::sui::SUI";
|
|
1119
1141
|
var USDC_TYPE_FULL = "0xdba34672e30cb065b1f93e3ab55318768fd6fef66c15942c9f7cb846e2f900e7::usdc::USDC";
|
|
1120
1142
|
var BLUE_TYPE_FULL = "0xe1b45a0e641b9955a20aa0ad1c1f4ad86aad8afb07296d4085e349a50e90bdca::blue::BLUE";
|
|
@@ -1189,8 +1211,10 @@ async function fetchAddressDefiPortfolio(address, apiKey, priceHints = {}, opts
|
|
|
1189
1211
|
}
|
|
1190
1212
|
const stickyBasis = recheck ?? cachedEntry;
|
|
1191
1213
|
const fanoutAt = Date.now();
|
|
1192
|
-
const settled = await
|
|
1193
|
-
DEFI_PROTOCOLS
|
|
1214
|
+
const settled = await mapWithConcurrency(
|
|
1215
|
+
DEFI_PROTOCOLS,
|
|
1216
|
+
(p) => fetchOneDefiProtocol(address, p, apiKey, opts.retryStats),
|
|
1217
|
+
DEFI_PROTOCOL_CONCURRENCY
|
|
1194
1218
|
);
|
|
1195
1219
|
const seen = /* @__PURE__ */ new Set();
|
|
1196
1220
|
for (const s of settled) {
|
|
@@ -1668,7 +1692,7 @@ async function fetchAudricHistory(address, opts, env, signal) {
|
|
|
1668
1692
|
}
|
|
1669
1693
|
}
|
|
1670
1694
|
|
|
1671
|
-
// src/sui
|
|
1695
|
+
// src/sui/address.ts
|
|
1672
1696
|
var SUI_MAINNET_URL2 = "https://fullnode.mainnet.sui.io:443";
|
|
1673
1697
|
var SUI_ADDRESS_REGEX = /^0x[a-fA-F0-9]{1,64}$/i;
|
|
1674
1698
|
var SUI_ADDRESS_STRICT_REGEX = /^0x[a-fA-F0-9]{64}$/i;
|
|
@@ -2113,7 +2137,7 @@ var balanceCheckTool = buildTool({
|
|
|
2113
2137
|
}
|
|
2114
2138
|
});
|
|
2115
2139
|
|
|
2116
|
-
// src/navi
|
|
2140
|
+
// src/navi/cache.ts
|
|
2117
2141
|
var InMemoryNaviCacheStore = class {
|
|
2118
2142
|
store = /* @__PURE__ */ new Map();
|
|
2119
2143
|
async get(key) {
|
|
@@ -2156,7 +2180,7 @@ var naviKey = {
|
|
|
2156
2180
|
health: (address) => `navi:health:${address.toLowerCase()}`
|
|
2157
2181
|
};
|
|
2158
2182
|
|
|
2159
|
-
// src/navi
|
|
2183
|
+
// src/navi/reads.ts
|
|
2160
2184
|
function sn(opts) {
|
|
2161
2185
|
return opts?.serverName ?? NAVI_SERVER_NAME;
|
|
2162
2186
|
}
|
|
@@ -3365,7 +3389,25 @@ var claimRewardsTool = buildTool({
|
|
|
3365
3389
|
flags: { mutating: true },
|
|
3366
3390
|
async call(_input, context) {
|
|
3367
3391
|
const agent = requireAgent(context);
|
|
3368
|
-
|
|
3392
|
+
let result;
|
|
3393
|
+
try {
|
|
3394
|
+
result = await agent.claimRewards();
|
|
3395
|
+
} catch (err) {
|
|
3396
|
+
const errAny = err;
|
|
3397
|
+
const isProtocolDown = errAny?.code === "PROTOCOL_UNAVAILABLE";
|
|
3398
|
+
const detail = typeof errAny?.message === "string" ? errAny.message.replace(/^[^:]*:\s*/, "") : "";
|
|
3399
|
+
const displayText2 = isProtocolDown ? `Could not check pending rewards \u2014 NAVI is degraded right now${detail ? ` (${detail.slice(0, 80)})` : ""}. Try again in a moment.` : "Could not check pending rewards \u2014 protocol error. Try again in a moment.";
|
|
3400
|
+
const data2 = {
|
|
3401
|
+
success: false,
|
|
3402
|
+
tx: null,
|
|
3403
|
+
rewards: [],
|
|
3404
|
+
totalValueUsd: 0,
|
|
3405
|
+
gasCost: 0,
|
|
3406
|
+
degraded: true,
|
|
3407
|
+
degradationReason: errAny?.code ?? "UNKNOWN"
|
|
3408
|
+
};
|
|
3409
|
+
return { data: data2, displayText: displayText2 };
|
|
3410
|
+
}
|
|
3369
3411
|
const priceCache = context.priceCache;
|
|
3370
3412
|
const enrichedRewards = result.rewards.map((r) => {
|
|
3371
3413
|
if (r.estimatedValueUsd > 0) return r;
|
|
@@ -3387,16 +3429,16 @@ var claimRewardsTool = buildTool({
|
|
|
3387
3429
|
const txSuffix = txShort ? ` (tx: ${txShort})` : "";
|
|
3388
3430
|
displayText = `Claimed ${breakdown}${usdSuffix}${txSuffix}`;
|
|
3389
3431
|
}
|
|
3390
|
-
|
|
3391
|
-
|
|
3392
|
-
|
|
3393
|
-
|
|
3394
|
-
|
|
3395
|
-
|
|
3396
|
-
|
|
3397
|
-
|
|
3398
|
-
displayText
|
|
3432
|
+
const data = {
|
|
3433
|
+
success: result.success,
|
|
3434
|
+
tx: result.tx || null,
|
|
3435
|
+
rewards: enrichedRewards,
|
|
3436
|
+
totalValueUsd,
|
|
3437
|
+
gasCost: result.gasCost,
|
|
3438
|
+
degraded: false,
|
|
3439
|
+
degradationReason: null
|
|
3399
3440
|
};
|
|
3441
|
+
return { data, displayText };
|
|
3400
3442
|
}
|
|
3401
3443
|
});
|
|
3402
3444
|
var MPP_GATEWAY = "https://mpp.t2000.ai";
|
|
@@ -5167,6 +5209,70 @@ var resolveSuinsTool = buildTool({
|
|
|
5167
5209
|
}
|
|
5168
5210
|
}
|
|
5169
5211
|
});
|
|
5212
|
+
function formatAmount2(amount) {
|
|
5213
|
+
if (!Number.isFinite(amount) || amount <= 0) return "0";
|
|
5214
|
+
if (amount >= 1) return amount.toFixed(4).replace(/\.?0+$/, "");
|
|
5215
|
+
if (amount >= 1e-4) return amount.toFixed(6).replace(/\.?0+$/, "");
|
|
5216
|
+
return amount.toExponential(2);
|
|
5217
|
+
}
|
|
5218
|
+
var pendingRewardsTool = buildTool({
|
|
5219
|
+
name: "pending_rewards",
|
|
5220
|
+
description: "Inspect unclaimed protocol rewards for the signed-in user without claiming them. Returns a per-asset breakdown \u2014 symbol, amount, USD value (when oracle prices are known) \u2014 plus the total claimable USD. Use BEFORE calling claim_rewards or harvest_rewards so you can tell the user exactly what's claimable; if zero rewards or NAVI is degraded, surface that truthfully instead of jumping to a write. Read-only, never opens a confirm card.",
|
|
5221
|
+
inputSchema: z.object({}),
|
|
5222
|
+
jsonSchema: { type: "object", properties: {}, required: [] },
|
|
5223
|
+
isReadOnly: true,
|
|
5224
|
+
// Rewards accrue continuously and are zeroed on claim — never dedupe
|
|
5225
|
+
// across turns within the same session.
|
|
5226
|
+
cacheable: false,
|
|
5227
|
+
async call(_input, context) {
|
|
5228
|
+
const agent = requireAgent(context);
|
|
5229
|
+
let rewards;
|
|
5230
|
+
try {
|
|
5231
|
+
rewards = await agent.getPendingRewards();
|
|
5232
|
+
} catch (err) {
|
|
5233
|
+
const errAny = err;
|
|
5234
|
+
const isProtocolDown = errAny?.code === "PROTOCOL_UNAVAILABLE";
|
|
5235
|
+
const detail = typeof errAny?.message === "string" ? errAny.message.replace(/^[^:]*:\s*/, "") : "";
|
|
5236
|
+
const displayText2 = isProtocolDown ? `Could not check pending rewards \u2014 NAVI is degraded right now${detail ? ` (${detail.slice(0, 80)})` : ""}. Try again in a moment.` : "Could not check pending rewards \u2014 protocol error. Try again in a moment.";
|
|
5237
|
+
const data2 = {
|
|
5238
|
+
rewards: [],
|
|
5239
|
+
totalValueUsd: 0,
|
|
5240
|
+
degraded: true,
|
|
5241
|
+
degradationReason: errAny?.code ?? "UNKNOWN"
|
|
5242
|
+
};
|
|
5243
|
+
return { data: data2, displayText: displayText2 };
|
|
5244
|
+
}
|
|
5245
|
+
const priceCache = context.priceCache;
|
|
5246
|
+
const enriched = rewards.map((r) => {
|
|
5247
|
+
if (r.estimatedValueUsd > 0) return r;
|
|
5248
|
+
const price = priceCache?.get(r.symbol.toUpperCase());
|
|
5249
|
+
if (!price || !Number.isFinite(price) || price <= 0) return r;
|
|
5250
|
+
return { ...r, estimatedValueUsd: r.amount * price };
|
|
5251
|
+
});
|
|
5252
|
+
const totalValueUsd = enriched.reduce(
|
|
5253
|
+
(s, r) => s + (Number.isFinite(r.estimatedValueUsd) ? r.estimatedValueUsd : 0),
|
|
5254
|
+
0
|
|
5255
|
+
);
|
|
5256
|
+
let displayText;
|
|
5257
|
+
if (enriched.length === 0) {
|
|
5258
|
+
displayText = "No pending rewards.";
|
|
5259
|
+
} else {
|
|
5260
|
+
const breakdown = enriched.map((r) => {
|
|
5261
|
+
const usd = Number.isFinite(r.estimatedValueUsd) && r.estimatedValueUsd > 0 ? ` (~$${r.estimatedValueUsd.toFixed(r.estimatedValueUsd >= 1 ? 2 : 4)})` : "";
|
|
5262
|
+
return `${formatAmount2(r.amount)} ${r.symbol}${usd}`;
|
|
5263
|
+
}).join(", ");
|
|
5264
|
+
const totalSuffix = totalValueUsd > 0 ? ` \u2014 total ~$${totalValueUsd.toFixed(2)}` : "";
|
|
5265
|
+
displayText = `Pending rewards: ${breakdown}${totalSuffix}`;
|
|
5266
|
+
}
|
|
5267
|
+
const data = {
|
|
5268
|
+
rewards: enriched,
|
|
5269
|
+
totalValueUsd,
|
|
5270
|
+
degraded: false,
|
|
5271
|
+
degradationReason: null
|
|
5272
|
+
};
|
|
5273
|
+
return { data, displayText };
|
|
5274
|
+
}
|
|
5275
|
+
});
|
|
5170
5276
|
var todoStatusSchema = z.enum(["pending", "in_progress", "completed"]);
|
|
5171
5277
|
var todoItemSchema = z.object({
|
|
5172
5278
|
id: z.string().min(1, "id must be a non-empty string").max(40, "id must be \u226440 chars (use a slug, not a sentence)"),
|
|
@@ -5448,7 +5554,8 @@ var READ_TOOLS = [
|
|
|
5448
5554
|
spendingAnalyticsTool,
|
|
5449
5555
|
yieldSummaryTool,
|
|
5450
5556
|
activitySummaryTool,
|
|
5451
|
-
resolveSuinsTool
|
|
5557
|
+
resolveSuinsTool,
|
|
5558
|
+
pendingRewardsTool
|
|
5452
5559
|
];
|
|
5453
5560
|
var WRITE_TOOLS = [
|
|
5454
5561
|
saveDepositTool,
|
|
@@ -5501,8 +5608,11 @@ function getModifiableFields(toolName) {
|
|
|
5501
5608
|
return TOOL_MODIFIABLE_FIELDS[toolName];
|
|
5502
5609
|
}
|
|
5503
5610
|
|
|
5504
|
-
// src/prompt.ts
|
|
5505
|
-
var
|
|
5611
|
+
// src/prompt/index.ts
|
|
5612
|
+
var READ_COUNT = READ_TOOLS.length;
|
|
5613
|
+
var WRITE_COUNT = WRITE_TOOLS.length;
|
|
5614
|
+
var TOTAL_COUNT = READ_COUNT + WRITE_COUNT;
|
|
5615
|
+
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 ${TOTAL_COUNT} tools, Reasoning Engine with 14 guards and 6 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 40+ 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.
|
|
5506
5616
|
|
|
5507
5617
|
## Response rules
|
|
5508
5618
|
- 1-2 sentences max. No bullet lists unless asked. No preambles.
|
|
@@ -6739,7 +6849,7 @@ function safeNum(v) {
|
|
|
6739
6849
|
return isNaN(n) ? 0 : n;
|
|
6740
6850
|
}
|
|
6741
6851
|
|
|
6742
|
-
// src/turn-read
|
|
6852
|
+
// src/cache/turn-read.ts
|
|
6743
6853
|
var TurnReadCache = class {
|
|
6744
6854
|
store = /* @__PURE__ */ new Map();
|
|
6745
6855
|
/**
|
|
@@ -9171,7 +9281,7 @@ function parseEvalSummary(thinkingText) {
|
|
|
9171
9281
|
};
|
|
9172
9282
|
}
|
|
9173
9283
|
|
|
9174
|
-
// src/prompt
|
|
9284
|
+
// src/prompt/cache.ts
|
|
9175
9285
|
function buildCachedSystemPrompt(staticParts, dynamicPart) {
|
|
9176
9286
|
const blocks = staticParts.map((text, i) => ({
|
|
9177
9287
|
type: "text",
|
|
@@ -9315,7 +9425,7 @@ function buildStateContext(state) {
|
|
|
9315
9425
|
}
|
|
9316
9426
|
}
|
|
9317
9427
|
|
|
9318
|
-
// src/mcp.ts
|
|
9428
|
+
// src/mcp/index.ts
|
|
9319
9429
|
function buildMcpTools(context, tools) {
|
|
9320
9430
|
const engineTools = tools ?? getDefaultTools();
|
|
9321
9431
|
return engineTools.map((tool) => ({
|