thinyai 0.1.11 → 0.1.13

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.
Files changed (2) hide show
  1. package/dist/bin.js +65 -19
  2. package/package.json +7 -7
package/dist/bin.js CHANGED
@@ -1535,18 +1535,13 @@ function explorerTxUrl(network, digest) {
1535
1535
  function suiPlugin(opts) {
1536
1536
  const sig = opts.signer;
1537
1537
  const resolve2 = typeof sig === "function" ? sig : () => sig;
1538
- const signerOrThrow = () => {
1539
- const s = resolve2();
1540
- if (!s) {
1541
- throw new Error(
1542
- "No Sui wallet configured yet. Call sui_setup to create or import one (or tell the user to run `thiny sui init`)."
1543
- );
1544
- }
1545
- return s;
1538
+ const SETUP_NEEDED = {
1539
+ ok: false,
1540
+ setupNeeded: true,
1541
+ message: "Sui isn't set up yet. Ask the user which network (testnet or mainnet) and which wallet (generate a new key, import a private key, or use a Rill agent wallet), then call sui_setup. Do NOT retry this tool until setup is complete."
1546
1542
  };
1547
1543
  const requireSimSuccess = opts.policy?.requireSimSuccess ?? true;
1548
- const executeTx = async (tx, toolName, approvalArgs, reason) => {
1549
- const signer = signerOrThrow();
1544
+ const executeTx = async (signer, tx, toolName, approvalArgs, reason) => {
1550
1545
  const sim = await signer.devInspect(tx);
1551
1546
  const status = sim.effects.status.status;
1552
1547
  if (requireSimSuccess && status !== "success") {
@@ -1576,10 +1571,11 @@ function suiPlugin(opts) {
1576
1571
  coinType: z4.string().optional().describe("Coin type, e.g. 0x2::sui::SUI (default: SUI).")
1577
1572
  }),
1578
1573
  execute: async ({ address, coinType }) => {
1579
- const signer = signerOrThrow();
1574
+ const signer = resolve2();
1575
+ if (!signer) return SETUP_NEEDED;
1580
1576
  const owner = address ?? signer.address;
1581
1577
  if (owner === void 0) {
1582
- throw new Error("sui_balance: no address given and the signer has no key/address.");
1578
+ return { ok: false, message: "No address given and no wallet is set up. Run sui_setup first." };
1583
1579
  }
1584
1580
  const bal = await signer.client.getBalance({ owner, ...coinType ? { coinType } : {} });
1585
1581
  return {
@@ -1595,7 +1591,8 @@ function suiPlugin(opts) {
1595
1591
  description: "Read a Sui object's type and fields by id.",
1596
1592
  parameters: z4.object({ objectId: z4.string().min(1).describe("The object id (0x\u2026).") }),
1597
1593
  execute: async ({ objectId }) => {
1598
- const signer = signerOrThrow();
1594
+ const signer = resolve2();
1595
+ if (!signer) return SETUP_NEEDED;
1599
1596
  const res = await signer.client.getObject({
1600
1597
  id: objectId,
1601
1598
  options: { showContent: true, showType: true, showOwner: true }
@@ -1617,8 +1614,10 @@ function suiPlugin(opts) {
1617
1614
  unsignedTx: z4.string().min(1).describe("Unsigned PTB \u2014 the JSON string from Transaction.toJSON() (no sender, no gas).")
1618
1615
  }),
1619
1616
  execute: async ({ unsignedTx }) => {
1617
+ const signer = resolve2();
1618
+ if (!signer) return SETUP_NEEDED;
1620
1619
  const tx = Transaction2.from(unsignedTx);
1621
- return await executeTx(tx, "sui_execute_ptb", { unsignedTx }, "sign and submit a Sui PTB");
1620
+ return await executeTx(signer, tx, "sui_execute_ptb", { unsignedTx }, "sign and submit a Sui PTB");
1622
1621
  }
1623
1622
  });
1624
1623
  const transfer = defineTool({
@@ -1631,7 +1630,8 @@ function suiPlugin(opts) {
1631
1630
  coinType: z4.string().optional().describe("Coin type (default 0x2::sui::SUI).")
1632
1631
  }),
1633
1632
  execute: async ({ recipient, amountMist, coinType }) => {
1634
- const signer = signerOrThrow();
1633
+ const signer = resolve2();
1634
+ if (!signer) return SETUP_NEEDED;
1635
1635
  const amount = BigInt(amountMist);
1636
1636
  const type = coinType ?? "0x2::sui::SUI";
1637
1637
  const tx = new Transaction2();
@@ -1640,7 +1640,7 @@ function suiPlugin(opts) {
1640
1640
  tx.transferObjects([coin], tx.pure.address(recipient));
1641
1641
  } else {
1642
1642
  const owner = signer.address;
1643
- if (owner === void 0) throw new Error("sui_transfer: signer has no address.");
1643
+ if (owner === void 0) return { ok: false, message: "Wallet has no address. Run sui_setup." };
1644
1644
  const { data } = await signer.client.getCoins({ owner, coinType: type });
1645
1645
  const [first, ...rest] = data;
1646
1646
  if (!first) throw new Error(`sui_transfer: no ${type} coins owned by ${owner}.`);
@@ -1652,6 +1652,7 @@ function suiPlugin(opts) {
1652
1652
  tx.transferObjects([coin], tx.pure.address(recipient));
1653
1653
  }
1654
1654
  return executeTx(
1655
+ signer,
1655
1656
  tx,
1656
1657
  "sui_transfer",
1657
1658
  { recipient, amountMist, coinType: type },
@@ -1675,6 +1676,8 @@ function suiPlugin(opts) {
1675
1676
  ).optional().describe("Ordered arguments to the function.")
1676
1677
  }),
1677
1678
  execute: async ({ target, typeArguments, args }) => {
1679
+ const signer = resolve2();
1680
+ if (!signer) return SETUP_NEEDED;
1678
1681
  const tx = new Transaction2();
1679
1682
  const built = (args ?? []).map((a) => {
1680
1683
  if (a.kind === "gas") return tx.gas;
@@ -1705,7 +1708,7 @@ function suiPlugin(opts) {
1705
1708
  }
1706
1709
  });
1707
1710
  tx.moveCall({ target, typeArguments: typeArguments ?? [], arguments: built });
1708
- return await executeTx(tx, "sui_move_call", { target, typeArguments, args }, `Move call ${target}`);
1711
+ return await executeTx(signer, tx, "sui_move_call", { target, typeArguments, args }, `Move call ${target}`);
1709
1712
  }
1710
1713
  });
1711
1714
  return { name: "sui", tools: [balance, object, executePtb, transfer, moveCall] };
@@ -2796,12 +2799,55 @@ async function runCli() {
2796
2799
  return { activeAddress: address, note: `Now signing as ${address}.` };
2797
2800
  }
2798
2801
  });
2802
+ const suiBalancesTool = defineTool({
2803
+ name: "sui_balances",
2804
+ description: "Fetch ALL coin balances across ALL of the user's Sui addresses on a network. Use for 'what's my balance / what coins do I have'. Returns each address with its coins. SUI amounts are also given in whole SUI (1 SUI = 1e9 MIST).",
2805
+ parameters: z7.object({
2806
+ network: z7.enum(["testnet", "mainnet"]).optional().describe("Network (default: the active one)."),
2807
+ address: z7.string().optional().describe("Limit to one address (default: all the user's wallets).")
2808
+ }),
2809
+ execute: async ({ network: network2, address }) => {
2810
+ const net = network2 ?? suiNetwork;
2811
+ const client = net === suiNetwork && suiSignerRef ? suiSignerRef.client : suiSigner({ network: net }).client;
2812
+ const walletAddrs = suiWalletsOf(loadConfig()).map((w) => w.address);
2813
+ if (suiSignerRef?.address && !walletAddrs.includes(suiSignerRef.address)) {
2814
+ walletAddrs.push(suiSignerRef.address);
2815
+ }
2816
+ const addrs = address ? [address] : walletAddrs;
2817
+ if (addrs.length === 0) {
2818
+ return {
2819
+ ok: false,
2820
+ setupNeeded: true,
2821
+ message: "Sui isn't set up. Ask the user which network and wallet (generate / import / Rill), then call sui_setup. Don't retry until then."
2822
+ };
2823
+ }
2824
+ const addresses = [];
2825
+ for (const addr of addrs) {
2826
+ const balances = await client.getAllBalances({ owner: addr });
2827
+ addresses.push({
2828
+ address: addr,
2829
+ coins: balances.map((b) => {
2830
+ const symbol = b.coinType.split("::").pop() ?? b.coinType;
2831
+ const isSui = b.coinType.endsWith("::sui::SUI");
2832
+ return {
2833
+ symbol,
2834
+ coinType: b.coinType,
2835
+ balanceMist: b.totalBalance,
2836
+ ...isSui ? { sui: Number(b.totalBalance) / 1e9 } : {}
2837
+ };
2838
+ })
2839
+ });
2840
+ }
2841
+ return { network: net, addresses };
2842
+ }
2843
+ });
2799
2844
  const walletTools = [
2800
2845
  suiWalletsTool,
2801
2846
  suiCreateWalletTool,
2802
2847
  suiImportWalletTool,
2803
2848
  suiExportWalletTool,
2804
- suiUseWalletTool
2849
+ suiUseWalletTool,
2850
+ suiBalancesTool
2805
2851
  ];
2806
2852
  const fetchUrlTool = defineTool({
2807
2853
  name: "fetch_url",
@@ -2874,7 +2920,7 @@ HOW TO ACT: When a request maps to one of your tools, CALL THE TOOL automaticall
2874
2920
  YOUR TOOLS:
2875
2921
  \u2022 Memory \u2014 remember_fact: durable memory across sessions. Whenever the user shares anything durable about themselves (name, role, preferences, projects, goals), call remember_fact ONCE to save it. Your known facts are AUTO-INJECTED at the top of every conversation under \u201C[User Memory \u2026]\u201D, so answer \u201Cwhat do you remember / what's my name\u201D directly from that context \u2014 do NOT call recall_memory unless the injected memory is empty and you truly need to re-check. You DO remember across sessions; never say otherwise.
2876
2922
  \u2022 Links \u2014 fetch_url: read ANY URL the user shares (a skill.md, docs, JSON, an API/MCP endpoint). Always fetch shared links instead of saying you can't open URLs.
2877
- ` + (webSearchOn ? "\u2022 Web search \u2014 web_search: search the web for anything you don't know (news, prices, docs). web_search FINDS pages by query; fetch_url READS a specific URL \u2014 use them together.\n" : "") + "\u2022 Planning \u2014 update_plan (track multi-step work), delegate_task (hand a focused subtask to a sub-agent).\n\u2022 Sui blockchain \u2014 you transact yourself; NEVER tell the user to install a browser wallet. " + (suiSignerRef ? `The active wallet is on ${suiNetwork} at ${suiSignerRef.address ?? "?"}. ` : "No wallet yet \u2014 call sui_create_wallet (or sui_import_wallet) when the user wants Sui, then have them fund the address. ") + "Wallets: sui_wallets (list ALL the user's wallets + addresses \u2014 use this to answer 'what's my address / what wallets do I have'), sui_create_wallet (new key pair), sui_import_wallet (restore from a suiprivkey), sui_export_wallet (reveal a private key \u2014 only when asked), sui_use_wallet (switch the active wallet). On-chain: sui_balance & sui_object (read), sui_transfer (send SUI/any coin \u2014 amounts in MIST, 1 SUI = 1e9), sui_move_call (call ANY Move function), sui_execute_ptb (sign a builder/Rill PTB). Prefer sui_transfer for sends and sui_move_call for contract calls; confirm details before signing.",
2923
+ ` + (webSearchOn ? "\u2022 Web search \u2014 web_search: search the web for anything you don't know (news, prices, docs). web_search FINDS pages by query; fetch_url READS a specific URL \u2014 use them together.\n" : "") + "\u2022 Planning \u2014 update_plan (track multi-step work), delegate_task (hand a focused subtask to a sub-agent).\n\u2022 Sui blockchain \u2014 you transact yourself; NEVER tell the user to install a browser wallet. " + (suiSignerRef ? `Sui IS set up. The user's primary (active) wallet is on ${suiNetwork} at ${suiSignerRef.address ?? "?"}. ` : "Sui is NOT set up yet. When the user wants anything Sui-related, FIRST ask if they'd like you to set it up, and ask which network (testnet/mainnet) and which wallet option (generate a new key, import a suiprivkey, or use a Rill agent wallet). Then call sui_setup with their choices. Do not attempt other Sui tools until setup succeeds. ") + "Wallet tools: sui_wallets (list ALL wallets + which is primary/active \u2014 answer 'what's my address' from this), sui_balances (ALL coins across ALL addresses on a network \u2014 answer 'what's my balance' with this), sui_create_wallet, sui_import_wallet, sui_export_wallet (reveal a key only when asked), sui_use_wallet (switch primary). Never overwrite or replace an existing wallet \u2014 adding a wallet keeps the others. If it's unclear which wallet to use, ask the user which is their primary. On-chain: sui_balance & sui_object (read), sui_transfer (send SUI/any coin \u2014 amounts in MIST, 1 SUI = 1e9), sui_move_call (call ANY Move function), sui_execute_ptb (sign a builder/Rill PTB). Prefer sui_transfer for sends and sui_move_call for contract calls; confirm details before signing.\nWhen ANY Sui tool fails, do NOT paste raw JSON or stack traces \u2014 explain what went wrong in one plain sentence and what to do next. Present balances/results readably (SUI amounts in whole SUI, short addresses), and keep every answer brief and to the point.",
2878
2924
  tools: [echoTool, suiSetupTool, ...walletTools, fetchUrlTool, ...webTools],
2879
2925
  plugins: [
2880
2926
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "thinyai",
3
- "version": "0.1.11",
3
+ "version": "0.1.13",
4
4
  "description": "Thiny AI — a beautiful terminal agent: interactive chat, tools, Walrus memory, and Sui execution.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -38,16 +38,16 @@
38
38
  "tsup": "^8.5.1",
39
39
  "typescript": "^5.5.0",
40
40
  "@thiny/core": "0.1.0",
41
- "@thiny/logger-pino": "0.1.0",
42
41
  "@thiny/memory-memwal": "0.1.0",
43
- "@thiny/model-aisdk": "0.1.0",
42
+ "@thiny/logger-pino": "0.1.0",
44
43
  "@thiny/mcp": "0.1.0",
45
- "@thiny/plugin-sui": "0.1.0",
44
+ "@thiny/walrus": "0.1.0",
46
45
  "@thiny/plugin-agents": "0.1.0",
46
+ "@thiny/plugin-sui": "0.1.0",
47
47
  "@thiny/plugin-web-search": "0.1.0",
48
- "@thiny/walrus": "0.1.0",
49
- "@thiny/signer-sui": "0.1.0",
50
- "@thiny/skills": "0.1.0"
48
+ "@thiny/model-aisdk": "0.1.0",
49
+ "@thiny/skills": "0.1.0",
50
+ "@thiny/signer-sui": "0.1.0"
51
51
  },
52
52
  "author": "Thiny AI",
53
53
  "engines": {