@t2000/engine 1.24.5 → 1.24.7

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 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 { PendingReward as PendingReward$1, T2000 } from '@t2000/sdk';
6
+ import { PendingReward as PendingReward$1, SwapQuoteResult, T2000 } from '@t2000/sdk';
7
7
 
8
8
  type ProactiveType = 'idle_balance' | 'hf_warning' | 'apy_drift' | 'goal_progress';
9
9
  interface ProactiveMarker {
@@ -2710,6 +2710,18 @@ interface MicrocompactResult extends Array<Message> {
2710
2710
  * later call with identical inputs gets replaced — necessary because
2711
2711
  * their results depend on mutable on-chain state that writes invalidate.
2712
2712
  *
2713
+ * [v1.24.6 / S.122] Tools whose `flags.mutating === true` are ALSO
2714
+ * implicitly non-cacheable. Each call to a write tool produces a NEW
2715
+ * on-chain transaction (different digest, different balance changes, real
2716
+ * state mutation) — replacing the second result with "[Same result as
2717
+ * call #N — identical inputs]" lies to the LLM, which then narrates
2718
+ * "transaction deduplicated" to the user even though the on-chain write
2719
+ * actually settled. Surfaced during S.121 smoke testing: a second
2720
+ * `send_transfer` with identical inputs produced a real on-chain tx but
2721
+ * the engine narrated as if it had been skipped. Explicit `cacheable`
2722
+ * still wins (a `cacheable: true` write would be a tool-author bug, but
2723
+ * we don't override it), so the rule is: mutating ⇒ default `false`.
2724
+ *
2713
2725
  * Returns a new array — does not mutate the input. The returned array
2714
2726
  * carries a `dedupedToolUseIds` property listing every tool-use ID whose
2715
2727
  * tool_result block was replaced with a back-reference this pass.
@@ -3411,13 +3423,7 @@ declare const mppServicesTool: Tool<{
3411
3423
  mode?: "summary" | "full" | undefined;
3412
3424
  }, Record<string, unknown>>;
3413
3425
 
3414
- declare const swapExecuteTool: Tool<{
3415
- to: string;
3416
- amount: number;
3417
- from: string;
3418
- byAmountIn?: boolean | undefined;
3419
- slippage?: number | undefined;
3420
- }, {
3426
+ interface SwapExecuteSuccessData {
3421
3427
  tx: string;
3422
3428
  fromToken: string;
3423
3429
  toToken: string;
@@ -3426,14 +3432,33 @@ declare const swapExecuteTool: Tool<{
3426
3432
  priceImpact: number;
3427
3433
  route: string;
3428
3434
  gasCost: number;
3429
- }>;
3435
+ }
3436
+ type SwapExecuteToolResult = SwapExecuteSuccessData | {
3437
+ error: string;
3438
+ errorCode: 'ASSET_NOT_SUPPORTED' | 'SWAP_FAILED';
3439
+ hint: string;
3440
+ recoverable: true;
3441
+ };
3442
+ declare const swapExecuteTool: Tool<{
3443
+ to: string;
3444
+ amount: number;
3445
+ from: string;
3446
+ byAmountIn?: boolean | undefined;
3447
+ slippage?: number | undefined;
3448
+ }, SwapExecuteToolResult>;
3430
3449
 
3450
+ type SwapQuoteToolResult = SwapQuoteResult | {
3451
+ error: string;
3452
+ errorCode: 'ASSET_NOT_SUPPORTED' | 'SWAP_FAILED';
3453
+ hint: string;
3454
+ recoverable: true;
3455
+ };
3431
3456
  declare const swapQuoteTool: Tool<{
3432
3457
  to: string;
3433
3458
  amount: number;
3434
3459
  from: string;
3435
3460
  byAmountIn?: boolean | undefined;
3436
- }, _t2000_sdk.SwapQuoteResult>;
3461
+ }, SwapQuoteToolResult>;
3437
3462
 
3438
3463
  declare const voloStakeTool: Tool<{
3439
3464
  amount: number;
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { z } from 'zod';
2
- import { ALL_NAVI_ASSETS, getDecimalsForCoinType, resolveSymbol, isInRegistry, normalizeCoinType, assertAllowedAsset, normalizeAsset, SUPPORTED_ASSETS, getSwapQuote, getPendingRewardsByAddress, resolveTokenType, extractTransferDetails, classifyTransaction } from '@t2000/sdk';
2
+ import { ALL_NAVI_ASSETS, getDecimalsForCoinType, resolveSymbol, isInRegistry, normalizeCoinType, assertAllowedAsset, normalizeAsset, SUPPORTED_ASSETS, T2000Error, getSwapQuote, getPendingRewardsByAddress, resolveTokenType, extractTransferDetails, classifyTransaction } from '@t2000/sdk';
3
3
  import { randomUUID } from 'crypto';
4
4
  import { readdirSync, readFileSync } from 'fs';
5
5
  import { join } from 'path';
@@ -3657,6 +3657,10 @@ var mppServicesTool = buildTool({
3657
3657
  };
3658
3658
  }
3659
3659
  });
3660
+ var RECOVERY_HINTS = {
3661
+ ASSET_NOT_SUPPORTED: "This token symbol is not in the standard registry. Call `navi_navi_search_tokens` with the symbol to find its full coin type, then retry `swap_execute` with the full type string.",
3662
+ SWAP_FAILED: "No route or insufficient liquidity for this pair. Try a different amount, a different intermediate token, or check `balance_check` to confirm the source token is held."
3663
+ };
3660
3664
  var swapExecuteTool = buildTool({
3661
3665
  name: "swap_execute",
3662
3666
  description: 'Swap tokens on Sui via Cetus Aggregator (20+ DEXs). Supports any token pair with liquidity. Use user-friendly names (SUI, USDC, CETUS, DEEP, etc.) or full coin types. Payment Intent: composable \u2014 when paired with another composable write in the same request (e.g. "swap to USDC and save", "swap and send to Mom"), emit all calls in the same assistant turn so the engine compiles them into one atomic Payment Intent the user signs once.',
@@ -3716,10 +3720,26 @@ var swapExecuteTool = buildTool({
3716
3720
  };
3717
3721
  } catch (err) {
3718
3722
  sink.counter("cetus.swap_execute_count", { outcome: "error" });
3723
+ if (err instanceof T2000Error && (err.code === "ASSET_NOT_SUPPORTED" || err.code === "SWAP_FAILED")) {
3724
+ const hint = RECOVERY_HINTS[err.code];
3725
+ return {
3726
+ data: {
3727
+ error: err.message,
3728
+ errorCode: err.code,
3729
+ hint,
3730
+ recoverable: true
3731
+ },
3732
+ displayText: err.code === "ASSET_NOT_SUPPORTED" ? `Token "${input.from}" not in standard registry \u2014 searching for full coin type.` : `No swap route available for ${input.from} \u2192 ${input.to}.`
3733
+ };
3734
+ }
3719
3735
  throw err;
3720
3736
  }
3721
3737
  }
3722
3738
  });
3739
+ var RECOVERY_HINTS2 = {
3740
+ ASSET_NOT_SUPPORTED: 'This token symbol is not in the standard registry. Call `navi_navi_search_tokens` with the symbol to find its full coin type, then retry `swap_quote` with the full type string (e.g. "0x83556...::spring_sui::SPRING_SUI").',
3741
+ SWAP_FAILED: "No route or insufficient liquidity for this pair. Try a different amount, a different intermediate token, or check `balance_check` to confirm the source token is held."
3742
+ };
3723
3743
  var swapQuoteTool = buildTool({
3724
3744
  name: "swap_quote",
3725
3745
  description: "Get a swap quote without executing. Shows expected output amount, price impact, and route. Use before swap_execute to preview a trade.",
@@ -3760,6 +3780,18 @@ var swapQuoteTool = buildTool({
3760
3780
  };
3761
3781
  } catch (err) {
3762
3782
  sink.counter("cetus.find_route_count", { outcome: "error" });
3783
+ if (err instanceof T2000Error && (err.code === "ASSET_NOT_SUPPORTED" || err.code === "SWAP_FAILED")) {
3784
+ const hint = RECOVERY_HINTS2[err.code];
3785
+ return {
3786
+ data: {
3787
+ error: err.message,
3788
+ errorCode: err.code,
3789
+ hint,
3790
+ recoverable: true
3791
+ },
3792
+ displayText: err.code === "ASSET_NOT_SUPPORTED" ? `Token "${input.from === input.to ? input.from : `${input.from}/${input.to}`}" not in standard registry \u2014 searching for full coin type.` : `No swap route available for ${input.from} \u2192 ${input.to}.`
3793
+ };
3794
+ }
3763
3795
  throw err;
3764
3796
  }
3765
3797
  }
@@ -5721,6 +5753,17 @@ Only offer to execute actions you have tools for. If you retrieved a quote, data
5721
5753
  - "Is protocol X safe?" / "Tell me about NAVI": protocol_deep_dive with the slug.
5722
5754
  - "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.
5723
5755
 
5756
+ ## Recoverable tool errors (deterministic recovery paths)
5757
+ - **\`swap_quote\` or \`swap_execute\` returns \`{ errorCode: 'ASSET_NOT_SUPPORTED', recoverable: true, hint: ... }\`**: the symbol isn't in the standard registry. Call \`navi_navi_search_tokens\` with the symbol \u2192 take the returned full coin type \u2192 retry the swap with that full coin type string (e.g. \`0x83556457...::spring_sui::SPRING_SUI\` instead of \`SSUI\`). Don't apologize, just recover.
5758
+ - **\`swap_quote\` returns \`{ errorCode: 'SWAP_FAILED', recoverable: true }\`**: no route or insufficient liquidity. Call \`balance_check\` to confirm the source token is held with the expected amount, then either: try a smaller amount, or ask the user if they want to swap via an intermediate token (e.g. via SUI).
5759
+ - **Always check \`recoverable: true\` first** \u2014 if a tool result has that flag, do the suggested next action without asking the user. Recovery is the agent's job.
5760
+
5761
+ ## Authentication (you CANNOT log users in or out)
5762
+ - You have NO tool to log users in, log users out, sign them in, or sign them out. You cannot end their session, switch accounts, or clear cookies.
5763
+ - If a user types "logout", "sign out", "log out", "exit", or any variant: tell them "Tap the avatar in the top-right and choose Sign Out" \u2014 do NOT narrate fake success. Saying "you're logged out" when they aren't is the worst possible behavior.
5764
+ - If a user types "login", "sign in", "log in": tell them "Tap Sign In with Google in the top-right" \u2014 same rule.
5765
+ - If their session has expired (you'll see \`_sessionExpired: true\` on a tool result, or a "Your sign-in session has expired" message): tell them to tap **Sign back in** \u2014 the session-expired card has a button right there. Don't tell them to "logout and log back in" \u2014 there's nothing to log out from.
5766
+
5724
5767
  ## Safety
5725
5768
  - Never encourage risky financial behavior.
5726
5769
  - Warn when health factor < 1.5.
@@ -6595,7 +6638,9 @@ function microcompact(messages, tools) {
6595
6638
  const cacheableByName = /* @__PURE__ */ new Map();
6596
6639
  if (tools) {
6597
6640
  for (const t of tools) {
6598
- cacheableByName.set(t.name, t.cacheable ?? true);
6641
+ const explicit = t.cacheable;
6642
+ const isMutating = t.flags?.mutating === true;
6643
+ cacheableByName.set(t.name, explicit ?? !isMutating);
6599
6644
  }
6600
6645
  }
6601
6646
  const dedupedToolUseIds = /* @__PURE__ */ new Set();
@@ -7073,6 +7118,8 @@ var EarlyToolDispatcher = class {
7073
7118
  }
7074
7119
  return result;
7075
7120
  });
7121
+ promise.catch(() => {
7122
+ });
7076
7123
  this.entries.push({ call, tool, promise, deduped: false, readAttemptCount });
7077
7124
  return true;
7078
7125
  }