@vultisig/cli 0.22.0 → 0.22.4

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 (3) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/dist/index.js +228 -171
  3. package/package.json +6 -6
package/CHANGELOG.md CHANGED
@@ -1,5 +1,29 @@
1
1
  # @vultisig/cli
2
2
 
3
+ ## 0.22.4
4
+
5
+ ### Patch Changes
6
+
7
+ - [#422](https://github.com/vultisig/vultisig-sdk/pull/422) [`a0a8496`](https://github.com/vultisig/vultisig-sdk/commit/a0a8496b0b87722910bfb9f44c940c4981b25faf) Thanks [@neavra](https://github.com/neavra)! - CLI agent executor now recognizes the mcp-ts `execute_*` tx_ready envelope shape (`txArgs.tx`) for `execute_send` and `execute_contract_call`. Previously the executor only handled mcp-go's older shapes (`swap_tx` / `send_tx` / `tx`) and silently skipped every mcp-ts payload, leaving local-dev parity broken against production. Multi-leg `execute_swap` envelopes (carrying `approvalTxArgs`) are explicitly rejected for now — multi-leg sequencing is a follow-up.
8
+
9
+ - Updated dependencies [[`6b75472`](https://github.com/vultisig/vultisig-sdk/commit/6b7547288f8594fcf8a9c71e46a5163d6b6cd727), [`613004f`](https://github.com/vultisig/vultisig-sdk/commit/613004f5fbce2658a439296ca249d3e031a58078), [`2e1bfb8`](https://github.com/vultisig/vultisig-sdk/commit/2e1bfb85417787a7cc5d497d35f6e76d2bb5a41a)]:
10
+ - @vultisig/core-chain@1.6.0
11
+
12
+ ## 0.22.3
13
+
14
+ ### Patch Changes
15
+
16
+ - [#405](https://github.com/vultisig/vultisig-sdk/pull/405) [`441f5bb`](https://github.com/vultisig/vultisig-sdk/commit/441f5bb9022321023a65d28a5941717ac7542bee) Thanks [@gomesalexandre](https://github.com/gomesalexandre)! - fix(ustc): dynamic burn-tax for TerraClassic USTC sends, case-insensitive coin id check
17
+
18
+ Replaces the hardcoded 1,000,000 uusd fee surcharge with a live LCD `tax_rate` + optional `tax_cap` query for TerraClassic USTC (uusd) sends.
19
+ - `computeUstcBurnTaxAmount()` fetches on-chain rate and cap, returns '0' when rate is zero (current post-UST-collapse governance state)
20
+ - Fail-open on LCD outage: falls back to '0' so sends are never blocked when burn tax is zero; ante handler rejects if rate is non-zero and LCD is down
21
+ - Case-insensitive `coin.id?.toLowerCase() === 'uusd'` guard to match `areEqualCoins` behavior
22
+ - 5 unit tests covering: rate=0, rate=1.2%, rate+cap, LCD outage, LUNC non-USTC exclusion
23
+
24
+ - Updated dependencies [[`441f5bb`](https://github.com/vultisig/vultisig-sdk/commit/441f5bb9022321023a65d28a5941717ac7542bee)]:
25
+ - @vultisig/sdk@0.22.3
26
+
3
27
  ## 0.22.0
4
28
 
5
29
  ### Minor Changes
package/dist/index.js CHANGED
@@ -1654,7 +1654,7 @@ var thorchainMidgardBaseUrl, POOL_ID_RE, assertValidPoolId, isValidPoolId, norma
1654
1654
  var init_pools = __esm({
1655
1655
  "../../packages/core/chain/dist/chains/cosmos/thor/lp/pools.js"() {
1656
1656
  init_queryUrl();
1657
- thorchainMidgardBaseUrl = "https://midgard.thorchain.network";
1657
+ thorchainMidgardBaseUrl = "https://gateway.liquify.com/chain/thorchain_midgard";
1658
1658
  POOL_ID_RE = /^[A-Z0-9]+\.[A-Z0-9]+(-[A-Z0-9]+)?$/;
1659
1659
  assertValidPoolId = (pool) => {
1660
1660
  if (typeof pool !== "string" || pool.length === 0) {
@@ -1864,7 +1864,7 @@ var init_cosmosRpcUrl = __esm({
1864
1864
  Terra: "https://terra-lcd.publicnode.com",
1865
1865
  TerraClassic: "https://terra-classic-lcd.publicnode.com",
1866
1866
  Noble: "https://noble-api.polkachu.com",
1867
- THORChain: "https://thornode.thorchain.network",
1867
+ THORChain: "https://gateway.liquify.com/chain/thorchain_api",
1868
1868
  MayaChain: "https://mayanode.mayachain.info",
1869
1869
  Akash: "https://akash-rest.publicnode.com"
1870
1870
  };
@@ -5931,6 +5931,7 @@ function sseErrorToMessage(value) {
5931
5931
  var AgentClient = class {
5932
5932
  baseUrl;
5933
5933
  authToken = null;
5934
+ profile = "";
5934
5935
  verbose = false;
5935
5936
  constructor(baseUrl) {
5936
5937
  this.baseUrl = baseUrl.replace(/\/+$/, "");
@@ -5938,13 +5939,21 @@ var AgentClient = class {
5938
5939
  setAuthToken(token) {
5939
5940
  this.authToken = token;
5940
5941
  }
5942
+ /** Set the billing-profile slug sent as X-Vultisig-Abe-Profile on every
5943
+ * request. Empty falls back to the backend's default profile. */
5944
+ setProfile(profile) {
5945
+ this.profile = profile;
5946
+ }
5947
+ profileHeader() {
5948
+ return this.profile ? { "X-Vultisig-Abe-Profile": this.profile } : {};
5949
+ }
5941
5950
  // ============================================================================
5942
5951
  // Authentication
5943
5952
  // ============================================================================
5944
5953
  async authenticate(req) {
5945
5954
  const res = await fetch(`${this.baseUrl}/auth/token`, {
5946
5955
  method: "POST",
5947
- headers: { "Content-Type": "application/json" },
5956
+ headers: { "Content-Type": "application/json", ...this.profileHeader() },
5948
5957
  body: JSON.stringify(req)
5949
5958
  });
5950
5959
  if (!res.ok) {
@@ -6001,7 +6010,8 @@ var AgentClient = class {
6001
6010
  headers: {
6002
6011
  "Content-Type": "application/json",
6003
6012
  Accept: "text/event-stream",
6004
- ...this.authToken ? { Authorization: `Bearer ${this.authToken}` } : {}
6013
+ ...this.authToken ? { Authorization: `Bearer ${this.authToken}` } : {},
6014
+ ...this.profileHeader()
6005
6015
  },
6006
6016
  body: JSON.stringify(req),
6007
6017
  signal
@@ -6015,7 +6025,6 @@ var AgentClient = class {
6015
6025
  }
6016
6026
  const result = {
6017
6027
  fullText: "",
6018
- actions: [],
6019
6028
  suggestions: [],
6020
6029
  transactions: [],
6021
6030
  message: null
@@ -6107,14 +6116,6 @@ var AgentClient = class {
6107
6116
  if (typeof title === "string") callbacks.onTitle?.(title);
6108
6117
  break;
6109
6118
  }
6110
- case "actions": {
6111
- if (this.verbose) process.stderr.write(`[SSE:actions] raw: ${data.slice(0, 1e3)}
6112
- `);
6113
- const actions = v1Data?.actions ?? parsed.actions ?? [];
6114
- result.actions.push(...actions);
6115
- callbacks.onActions?.(actions);
6116
- break;
6117
- }
6118
6119
  case "suggestions": {
6119
6120
  const suggestions = v1Data?.suggestions ?? parsed.suggestions ?? [];
6120
6121
  result.suggestions.push(...suggestions);
@@ -6170,8 +6171,6 @@ var AgentClient = class {
6170
6171
  return "tool_progress";
6171
6172
  case "data-title":
6172
6173
  return "title";
6173
- case "data-actions":
6174
- return "actions";
6175
6174
  case "data-suggestions":
6176
6175
  return "suggestions";
6177
6176
  case "data-tx_ready":
@@ -6187,18 +6186,6 @@ var AgentClient = class {
6187
6186
  }
6188
6187
  }
6189
6188
  // ============================================================================
6190
- // Calldata
6191
- // ============================================================================
6192
- async getCalldata(id) {
6193
- const res = await fetch(`${this.baseUrl}/agent/calldata/${id}`, {
6194
- headers: this.authToken ? { Authorization: `Bearer ${this.authToken}` } : {}
6195
- });
6196
- if (!res.ok) {
6197
- throw new Error(`Failed to resolve calldata_id ${id}: ${res.status} ${res.statusText}`);
6198
- }
6199
- return res.json();
6200
- }
6201
- // ============================================================================
6202
6189
  // Private helpers
6203
6190
  // ============================================================================
6204
6191
  async post(path4, body) {
@@ -6206,7 +6193,8 @@ var AgentClient = class {
6206
6193
  method: "POST",
6207
6194
  headers: {
6208
6195
  "Content-Type": "application/json",
6209
- ...this.authToken ? { Authorization: `Bearer ${this.authToken}` } : {}
6196
+ ...this.authToken ? { Authorization: `Bearer ${this.authToken}` } : {},
6197
+ ...this.profileHeader()
6210
6198
  },
6211
6199
  body: JSON.stringify(body)
6212
6200
  });
@@ -6221,7 +6209,8 @@ var AgentClient = class {
6221
6209
  method: "DELETE",
6222
6210
  headers: {
6223
6211
  "Content-Type": "application/json",
6224
- ...this.authToken ? { Authorization: `Bearer ${this.authToken}` } : {}
6212
+ ...this.authToken ? { Authorization: `Bearer ${this.authToken}` } : {},
6213
+ ...this.profileHeader()
6225
6214
  },
6226
6215
  body: JSON.stringify(body)
6227
6216
  });
@@ -6380,7 +6369,7 @@ function getNativeTokenDecimals(chain) {
6380
6369
  }
6381
6370
 
6382
6371
  // src/agent/executor.ts
6383
- import { Chain as Chain10, evmCall, fiatCurrencies as fiatCurrencies3, Vultisig as VultisigSdk } from "@vultisig/sdk";
6372
+ import { Chain as Chain10, evmCall, Vultisig as VultisigSdk } from "@vultisig/sdk";
6384
6373
 
6385
6374
  // ../../node_modules/viem/_esm/index.js
6386
6375
  init_formatUnits();
@@ -6542,15 +6531,11 @@ function sleep2(ms) {
6542
6531
 
6543
6532
  // src/agent/types.ts
6544
6533
  var AUTO_EXECUTE_ACTIONS = /* @__PURE__ */ new Set([
6545
- "add_chain",
6546
- "add_coin",
6547
- "remove_coin",
6548
- "remove_chain",
6549
- "address_book_add",
6550
- "address_book_remove",
6534
+ "vault_chain",
6535
+ "vault_coin",
6536
+ "address_book",
6551
6537
  "get_address_book",
6552
6538
  "get_balances",
6553
- "get_portfolio",
6554
6539
  "search_token",
6555
6540
  "list_vaults",
6556
6541
  "build_swap_tx",
@@ -6609,8 +6594,6 @@ var AgentExecutor = class _AgentExecutor {
6609
6594
  /** Held chain lock release functions, keyed by chain name */
6610
6595
  chainLockReleases = /* @__PURE__ */ new Map();
6611
6596
  evmLastBroadcast = /* @__PURE__ */ new Map();
6612
- /** Backend client for resolving calldata_id references. */
6613
- backendClient = null;
6614
6597
  constructor(vault, verbose = false, vaultId, vultisig) {
6615
6598
  this.vault = vault;
6616
6599
  this.verbose = verbose;
@@ -6622,9 +6605,6 @@ var AgentExecutor = class _AgentExecutor {
6622
6605
  setPassword(password) {
6623
6606
  this.password = password;
6624
6607
  }
6625
- setBackendClient(client) {
6626
- this.backendClient = client;
6627
- }
6628
6608
  /**
6629
6609
  * Store a server-built transaction (from tx_ready SSE event).
6630
6610
  * This allows sign_tx to find and sign it when the backend requests signing.
@@ -6637,7 +6617,15 @@ var AgentExecutor = class _AgentExecutor {
6637
6617
  `[executor] storeServerTransaction called, keys: ${Object.keys(txReadyData || {}).join(",")}
6638
6618
  `
6639
6619
  );
6640
- const nestedTx = txReadyData?.swap_tx || txReadyData?.send_tx || txReadyData?.tx;
6620
+ if (txReadyData?.approvalTxArgs) {
6621
+ if (this.verbose)
6622
+ process.stderr.write(
6623
+ `[executor] skipping multi-leg execute_swap envelope (approvalTxArgs present): not yet supported in sdk-cli \u2014 Phase B
6624
+ `
6625
+ );
6626
+ return false;
6627
+ }
6628
+ const nestedTx = extractNestedTx(txReadyData);
6641
6629
  if (nestedTx?.status === "error" || nestedTx?.error) {
6642
6630
  if (this.verbose)
6643
6631
  process.stderr.write(`[executor] skipping error tx_ready: ${nestedTx.error || "unknown error"}
@@ -6645,7 +6633,8 @@ var AgentExecutor = class _AgentExecutor {
6645
6633
  return false;
6646
6634
  }
6647
6635
  if (!nestedTx) {
6648
- if (this.verbose) process.stderr.write(`[executor] storeServerTransaction: no swap_tx/send_tx/tx found in data
6636
+ if (this.verbose)
6637
+ process.stderr.write(`[executor] storeServerTransaction: no swap_tx/send_tx/tx/txArgs.tx found in data
6649
6638
  `);
6650
6639
  return false;
6651
6640
  }
@@ -6703,16 +6692,10 @@ var AgentExecutor = class _AgentExecutor {
6703
6692
  switch (action.type) {
6704
6693
  case "get_balances":
6705
6694
  return this.getBalances(params);
6706
- case "get_portfolio":
6707
- return this.getPortfolio(params);
6708
- case "add_chain":
6709
- return this.addChain(params);
6710
- case "remove_chain":
6711
- return this.removeChain(params);
6712
- case "add_coin":
6713
- return this.addCoin(params);
6714
- case "remove_coin":
6715
- return this.removeCoin(params);
6695
+ case "vault_chain":
6696
+ return this.vaultChain(params);
6697
+ case "vault_coin":
6698
+ return this.vaultCoin(params);
6716
6699
  case "build_send_tx":
6717
6700
  return this.buildSendTx(params);
6718
6701
  case "build_swap_tx":
@@ -6724,10 +6707,8 @@ var AgentExecutor = class _AgentExecutor {
6724
6707
  return this.signTx(params);
6725
6708
  case "get_address_book":
6726
6709
  return this.getAddressBook(params);
6727
- case "address_book_add":
6728
- return this.addAddressBookEntry(params);
6729
- case "address_book_remove":
6730
- return this.removeAddressBookEntry(params);
6710
+ case "address_book":
6711
+ return this.addressBook(params);
6731
6712
  case "search_token":
6732
6713
  return this.searchToken(params);
6733
6714
  case "list_vaults":
@@ -6775,39 +6756,38 @@ var AgentExecutor = class _AgentExecutor {
6775
6756
  }
6776
6757
  return { balances: entries };
6777
6758
  }
6778
- async getPortfolio(params) {
6779
- const currencyRaw = String(params.currency ?? "USD").trim().toLowerCase();
6780
- const fiatCurrency = fiatCurrencies3.includes(currencyRaw) ? currencyRaw : "usd";
6781
- const portfolio = await this.vault.portfolio(fiatCurrency);
6782
- const chainFilter = params.chain;
6783
- const tickerFilter = params.ticker;
6784
- let rows = portfolio.balances.map((b) => ({
6785
- chain: b.chainId || "",
6786
- symbol: b.symbol || "",
6787
- amount: b.formattedAmount || b.amount?.toString() || "0",
6788
- decimals: b.decimals ?? 18,
6789
- raw_amount: b.amount,
6790
- fiatValue: b.fiatValue,
6791
- fiatCurrency: b.fiatCurrency ?? portfolio.currency
6792
- }));
6793
- if (chainFilter) {
6794
- const chain = resolveChain(chainFilter);
6795
- if (!chain) throw new Error(`Unknown chain: ${chainFilter}`);
6796
- rows = rows.filter((r) => r.chain.toLowerCase() === chain.toLowerCase());
6797
- }
6798
- if (tickerFilter) {
6799
- rows = rows.filter((r) => r.symbol.toLowerCase() === String(tickerFilter).toLowerCase());
6800
- }
6801
- return {
6802
- balances: rows,
6803
- totalValue: portfolio.totalValue,
6804
- currency: portfolio.currency
6805
- };
6806
- }
6807
6759
  // ============================================================================
6808
6760
  // Chain & Token Management
6809
6761
  // ============================================================================
6810
- async addChain(params) {
6762
+ // vault_chain dispatcher — backend shape:
6763
+ // { action: "add" | "remove", chains: [{ chain }] }
6764
+ // Single-chain (`chain` only) and legacy string arrays are tolerated for
6765
+ // forward compatibility / hand-rolled callers.
6766
+ async vaultChain(params) {
6767
+ const action = params.action;
6768
+ switch (action) {
6769
+ case "add":
6770
+ return this.addChainImpl(params);
6771
+ case "remove":
6772
+ return this.removeChainImpl(params);
6773
+ default:
6774
+ throw new Error(`vault_chain: unknown action: ${action ?? "(missing)"}`);
6775
+ }
6776
+ }
6777
+ // vault_coin dispatcher — backend shape:
6778
+ // { action: "add" | "remove", coins: [{ chain, ticker, contract_address?, decimals?, ... }] }
6779
+ async vaultCoin(params) {
6780
+ const action = params.action;
6781
+ switch (action) {
6782
+ case "add":
6783
+ return this.addCoinImpl(params);
6784
+ case "remove":
6785
+ return this.removeCoinImpl(params);
6786
+ default:
6787
+ throw new Error(`vault_coin: unknown action: ${action ?? "(missing)"}`);
6788
+ }
6789
+ }
6790
+ async addChainImpl(params) {
6811
6791
  const chains = params.chains;
6812
6792
  if (chains && Array.isArray(chains)) {
6813
6793
  const results = [];
@@ -6828,21 +6808,33 @@ var AgentExecutor = class _AgentExecutor {
6828
6808
  const address = await this.vault.address(chain);
6829
6809
  return { chain: chain.toString(), address, added: true };
6830
6810
  }
6831
- async removeChain(params) {
6811
+ async removeChainImpl(params) {
6812
+ const chains = params.chains;
6813
+ if (chains && Array.isArray(chains)) {
6814
+ const results = [];
6815
+ for (const c of chains) {
6816
+ const name = typeof c === "string" ? c : c.chain;
6817
+ const chain2 = resolveChain(name);
6818
+ if (!chain2) throw new Error(`Unknown chain: ${name}`);
6819
+ await this.vault.removeChain(chain2);
6820
+ results.push({ chain: chain2.toString() });
6821
+ }
6822
+ return { removed: results };
6823
+ }
6832
6824
  const chainName = params.chain;
6833
6825
  const chain = resolveChain(chainName);
6834
6826
  if (!chain) throw new Error(`Unknown chain: ${chainName}`);
6835
6827
  await this.vault.removeChain(chain);
6836
6828
  return { chain: chain.toString(), removed: true };
6837
6829
  }
6838
- async addCoin(params) {
6839
- const tokens = params.tokens;
6840
- if (tokens && Array.isArray(tokens)) {
6830
+ async addCoinImpl(params) {
6831
+ const coins = params.coins ?? params.tokens;
6832
+ if (coins && Array.isArray(coins)) {
6841
6833
  const results = [];
6842
- for (const t of tokens) {
6834
+ for (const t of coins) {
6843
6835
  const chain2 = resolveChain(t.chain);
6844
6836
  if (!chain2) throw new Error(`Unknown chain: ${t.chain}`);
6845
- const symbol2 = t.symbol || t.ticker || "";
6837
+ const symbol2 = t.ticker || t.symbol || "";
6846
6838
  await this.vault.addToken(chain2, {
6847
6839
  id: t.contract_address || t.contractAddress || "",
6848
6840
  symbol: symbol2,
@@ -6858,7 +6850,7 @@ var AgentExecutor = class _AgentExecutor {
6858
6850
  const chainName = params.chain;
6859
6851
  const chain = resolveChain(chainName);
6860
6852
  if (!chain) throw new Error(`Unknown chain: ${chainName}`);
6861
- const symbol = params.symbol || params.ticker;
6853
+ const symbol = params.ticker || params.symbol;
6862
6854
  await this.vault.addToken(chain, {
6863
6855
  id: params.contract_address || params.contractAddress || "",
6864
6856
  symbol,
@@ -6869,11 +6861,31 @@ var AgentExecutor = class _AgentExecutor {
6869
6861
  });
6870
6862
  return { chain: chain.toString(), symbol, added: true };
6871
6863
  }
6872
- async removeCoin(params) {
6864
+ async removeCoinImpl(params) {
6865
+ const coins = params.coins ?? params.tokens;
6866
+ if (coins && Array.isArray(coins)) {
6867
+ const results = [];
6868
+ for (const t of coins) {
6869
+ const chain2 = resolveChain(t.chain);
6870
+ if (!chain2) throw new Error(`Unknown chain: ${t.chain}`);
6871
+ const tokenId2 = t.contract_address || t.contractAddress || t.token_id || t.id;
6872
+ if (!tokenId2) {
6873
+ throw new Error(
6874
+ `vault_coin remove: missing contract_address for ${t.ticker || t.symbol || "coin"} on ${t.chain}`
6875
+ );
6876
+ }
6877
+ await this.vault.removeToken(chain2, tokenId2);
6878
+ results.push({ chain: chain2.toString(), tokenId: tokenId2 });
6879
+ }
6880
+ return { removed: results };
6881
+ }
6873
6882
  const chainName = params.chain;
6874
6883
  const chain = resolveChain(chainName);
6875
6884
  if (!chain) throw new Error(`Unknown chain: ${chainName}`);
6876
- const tokenId = params.token_id || params.id || params.contract_address;
6885
+ const tokenId = params.contract_address || params.contractAddress || params.token_id || params.id;
6886
+ if (!tokenId) {
6887
+ throw new Error(`vault_coin remove: missing contract_address for coin on ${chainName}`);
6888
+ }
6877
6889
  await this.vault.removeToken(chain, tokenId);
6878
6890
  return { chain: chain.toString(), removed: true };
6879
6891
  }
@@ -7235,17 +7247,6 @@ var AgentExecutor = class _AgentExecutor {
7235
7247
  return _AgentExecutor.THORCHAIN_RUNE_DEPOSIT_ADDRESS;
7236
7248
  }
7237
7249
  async buildTx(params) {
7238
- if (params.calldata_id && !params.data && this.backendClient) {
7239
- const id = params.calldata_id;
7240
- if (this.verbose) process.stderr.write(`[executor] resolving calldata_id ${id}
7241
- `);
7242
- const entry = await this.backendClient.getCalldata(id);
7243
- params = { ...params, data: entry.data };
7244
- if (!params.to && entry.to) params = { ...params, to: entry.to };
7245
- delete params.calldata_id;
7246
- if (this.verbose) process.stderr.write(`[executor] calldata_id resolved, data len=${entry.data.length}
7247
- `);
7248
- }
7249
7250
  if (params.function_name && params.contract_address) {
7250
7251
  return this.buildContractCallTx(params);
7251
7252
  }
@@ -7283,7 +7284,9 @@ var AgentExecutor = class _AgentExecutor {
7283
7284
  `build_custom_tx requires function_name and params for contract calls. Got: ${provided}. Missing: function_name, params.`
7284
7285
  );
7285
7286
  }
7286
- return this.buildSendTx(params);
7287
+ throw new Error(
7288
+ `build_custom_tx: unrecognized params shape. Expected function_name + contract_address for ABI-encoding. Server-built calldata should arrive via tx_ready, not via action params. Got keys: ${Object.keys(params).join(", ")}`
7289
+ );
7287
7290
  }
7288
7291
  /**
7289
7292
  * Build, sign, and broadcast an EVM contract call transaction from structured params.
@@ -7407,12 +7410,12 @@ var AgentExecutor = class _AgentExecutor {
7407
7410
  * Uses vault.prepareSendTx with memo field to carry the calldata.
7408
7411
  */
7409
7412
  async signServerTx(serverTxData, defaultChain, params) {
7410
- const swapTx = serverTxData.swap_tx || serverTxData.send_tx || serverTxData.tx;
7413
+ const swapTx = extractNestedTx(serverTxData);
7411
7414
  if (!swapTx?.to) {
7412
7415
  throw new Error("Server transaction missing required fields (to)");
7413
7416
  }
7414
- const chainName = params.chain || serverTxData.chain || serverTxData.from_chain;
7415
- const chainId = serverTxData.chain_id || swapTx.chainId;
7417
+ const chainName = params.chain || serverTxData.chain || serverTxData.from_chain || serverTxData.txArgs?.chain;
7418
+ const chainId = serverTxData.chain_id || serverTxData.txArgs?.chain_id || swapTx.chainId;
7416
7419
  let chain = defaultChain;
7417
7420
  if (chainName) {
7418
7421
  chain = resolveChain(chainName) || defaultChain;
@@ -7640,7 +7643,7 @@ var AgentExecutor = class _AgentExecutor {
7640
7643
  const nextNonce = this.stateStore.getNextEvmNonce(chain, rpcNonce);
7641
7644
  if (nextNonce !== rpcNonce) {
7642
7645
  const lastBroadcast = this.evmLastBroadcast.get(chain.toString()) ?? 0;
7643
- if (Date.now() - lastBroadcast < 15e3) {
7646
+ if (Date.now() - lastBroadcast < 3e4) {
7644
7647
  if (this.verbose)
7645
7648
  process.stderr.write(
7646
7649
  `[nonce] Keeping local nonce ${nextNonce} for ${chain} (broadcast ${Date.now() - lastBroadcast}ms ago)
@@ -7859,13 +7862,81 @@ var AgentExecutor = class _AgentExecutor {
7859
7862
  }
7860
7863
  return await this.vultisig.getAddressBook(chain);
7861
7864
  }
7862
- async addAddressBookEntry(_params) {
7863
- throw new Error("address_book_add is not yet implemented locally. The backend may handle this action server-side.");
7865
+ // address_book dispatcher — backend shape:
7866
+ // { action: "add" | "remove", entry: { name, chain, address } }
7867
+ async addressBook(params) {
7868
+ const action = params.action;
7869
+ switch (action) {
7870
+ case "add":
7871
+ return this.addAddressBookImpl(params);
7872
+ case "remove":
7873
+ return this.removeAddressBookImpl(params);
7874
+ default:
7875
+ throw new Error(`address_book: unknown action: ${action ?? "(missing)"}`);
7876
+ }
7864
7877
  }
7865
- async removeAddressBookEntry(_params) {
7866
- throw new Error(
7867
- "address_book_remove is not yet implemented locally. The backend may handle this action server-side."
7868
- );
7878
+ async addAddressBookImpl(params) {
7879
+ if (!this.vultisig) {
7880
+ throw new Error(
7881
+ "address_book add requires the CLI SDK instance. Ensure AgentConfig.vultisig is set when creating the session."
7882
+ );
7883
+ }
7884
+ const entry = params.entry;
7885
+ if (!entry || typeof entry !== "object") {
7886
+ throw new Error("address_book add: missing entry");
7887
+ }
7888
+ const chainName = entry.chain;
7889
+ const chain = chainName ? resolveChain(chainName) : void 0;
7890
+ if (!chain) throw new Error(`address_book add: unknown chain: ${chainName ?? "(missing)"}`);
7891
+ const address = entry.address;
7892
+ if (!address) throw new Error("address_book add: entry.address is required");
7893
+ const name = entry.name ?? "";
7894
+ await this.vultisig.addAddressBookEntry([
7895
+ {
7896
+ chain,
7897
+ address,
7898
+ name,
7899
+ source: "saved",
7900
+ dateAdded: Date.now()
7901
+ }
7902
+ ]);
7903
+ return { added: { chain: chain.toString(), address, name } };
7904
+ }
7905
+ async removeAddressBookImpl(params) {
7906
+ if (!this.vultisig) {
7907
+ throw new Error(
7908
+ "address_book remove requires the CLI SDK instance. Ensure AgentConfig.vultisig is set when creating the session."
7909
+ );
7910
+ }
7911
+ const entry = params.entry;
7912
+ if (!entry || typeof entry !== "object") {
7913
+ throw new Error("address_book remove: missing entry");
7914
+ }
7915
+ const chainName = entry.chain;
7916
+ const chain = chainName ? resolveChain(chainName) : void 0;
7917
+ if (!chain) throw new Error(`address_book remove: unknown chain: ${chainName ?? "(missing)"}`);
7918
+ let address = entry.address;
7919
+ if (!address) {
7920
+ const name = entry.name;
7921
+ if (!name) {
7922
+ throw new Error("address_book remove: entry.address or entry.name is required");
7923
+ }
7924
+ const book = await this.vultisig.getAddressBook(chain);
7925
+ const lower = name.toLowerCase();
7926
+ const matches = book.saved.filter((e) => e.name.toLowerCase() === lower && e.chain === chain);
7927
+ if (matches.length === 0) {
7928
+ throw new Error(`address_book remove: no saved entry named "${name}" on ${chainName}`);
7929
+ }
7930
+ if (matches.length > 1) {
7931
+ const addrs = matches.map((m) => m.address).join(", ");
7932
+ throw new Error(
7933
+ `address_book remove: ambiguous name "${name}" on ${chainName} \u2014 multiple addresses: ${addrs}. Specify entry.address explicitly.`
7934
+ );
7935
+ }
7936
+ address = matches[0].address;
7937
+ }
7938
+ await this.vultisig.removeAddressBookEntry([{ chain, address }]);
7939
+ return { removed: { chain: chain.toString(), address } };
7869
7940
  }
7870
7941
  // ============================================================================
7871
7942
  // Token Search & Other
@@ -8026,13 +8097,24 @@ function resolveChainFromTxReady(txReadyData) {
8026
8097
  const chain = resolveChainId(txReadyData.chain_id);
8027
8098
  if (chain) return chain;
8028
8099
  }
8029
- const swapTx = txReadyData.swap_tx || txReadyData.send_tx || txReadyData.tx;
8100
+ if (txReadyData.txArgs?.chain) {
8101
+ const chain = resolveChain(txReadyData.txArgs.chain);
8102
+ if (chain) return chain;
8103
+ }
8104
+ if (txReadyData.txArgs?.chain_id) {
8105
+ const chain = resolveChainId(txReadyData.txArgs.chain_id);
8106
+ if (chain) return chain;
8107
+ }
8108
+ const swapTx = extractNestedTx(txReadyData);
8030
8109
  if (swapTx?.chainId) {
8031
8110
  const chain = resolveChainId(swapTx.chainId);
8032
8111
  if (chain) return chain;
8033
8112
  }
8034
8113
  return null;
8035
8114
  }
8115
+ function extractNestedTx(txReadyData) {
8116
+ return txReadyData?.swap_tx || txReadyData?.send_tx || txReadyData?.tx || txReadyData?.txArgs?.tx;
8117
+ }
8036
8118
  function resolveChainId(chainId) {
8037
8119
  const id = typeof chainId === "string" ? parseInt(chainId, 10) : chainId;
8038
8120
  if (isNaN(id)) return null;
@@ -8427,12 +8509,9 @@ import { join as join2 } from "node:path";
8427
8509
  import { MemoryStorage, PushNotificationService } from "@vultisig/sdk";
8428
8510
  var CLIENT_SIDE_TOOL_DISPATCH = {
8429
8511
  sign_typed_data: "sign_typed_data",
8430
- add_coin: "add_coin",
8431
- remove_coin: "remove_coin",
8432
- add_chain: "add_chain",
8433
- remove_chain: "remove_chain",
8434
- address_book_add: "address_book_add",
8435
- address_book_remove: "address_book_remove"
8512
+ vault_coin: "vault_coin",
8513
+ vault_chain: "vault_chain",
8514
+ address_book: "address_book"
8436
8515
  };
8437
8516
  var MAX_MESSAGE_LOOP_DEPTH = 16;
8438
8517
  function actionResultToRecentAction(r) {
@@ -8462,6 +8541,9 @@ var AgentSession = class {
8462
8541
  this.config = config;
8463
8542
  this.client = new AgentClient(config.backendUrl);
8464
8543
  this.client.verbose = !!config.verbose;
8544
+ if (config.profile) {
8545
+ this.client.setProfile(config.profile);
8546
+ }
8465
8547
  this.executor = new AgentExecutor(vault, !!config.verbose, vault.publicKeys.ecdsa, config.vultisig);
8466
8548
  this.publicKey = vault.publicKeys.ecdsa;
8467
8549
  if (config.password) {
@@ -8490,7 +8572,6 @@ var AgentSession = class {
8490
8572
  this.client.setAuthToken(auth.token);
8491
8573
  saveCachedToken(this.publicKey, auth.token, auth.expiresAt);
8492
8574
  }
8493
- this.executor.setBackendClient(this.client);
8494
8575
  } catch (err) {
8495
8576
  throw new Error(`Authentication failed: ${err.message}`);
8496
8577
  }
@@ -8640,8 +8721,6 @@ var AgentSession = class {
8640
8721
  },
8641
8722
  onTitle: (_title) => {
8642
8723
  },
8643
- onActions: (_actions) => {
8644
- },
8645
8724
  onSuggestions: (suggestions) => {
8646
8725
  ui.onSuggestions(suggestions);
8647
8726
  },
@@ -8694,37 +8773,6 @@ var AgentSession = class {
8694
8773
  if (displayText) {
8695
8774
  ui.onAssistantMessage(displayText);
8696
8775
  }
8697
- const legacyActions = streamResult.actions.filter((a) => a.type !== "sign_tx");
8698
- if (legacyActions.length > 0) {
8699
- const results = await this.executeActions(legacyActions, ui);
8700
- const hasBuildSuccess = results.some((r) => r.success && r.action.startsWith("build_"));
8701
- if (hasBuildSuccess && this.executor.hasPendingTransaction()) {
8702
- if (this.config.verbose)
8703
- process.stderr.write(`[session] build_* action produced pending tx, auto-signing client-side
8704
- `);
8705
- const signAction = {
8706
- id: `tx_sign_${Date.now()}`,
8707
- type: "sign_tx",
8708
- title: "Sign transaction",
8709
- params: {},
8710
- auto_execute: true
8711
- };
8712
- const signResults = await this.executeActions([signAction], ui);
8713
- const signResult = signResults[0];
8714
- if (signResult) {
8715
- this.pendingToolResults.push(actionResultToRecentAction(signResult));
8716
- await this.processMessageLoop(null, ui, depth + 1);
8717
- return;
8718
- }
8719
- }
8720
- if (results.length > 0) {
8721
- for (const result of results) {
8722
- this.pendingToolResults.push(actionResultToRecentAction(result));
8723
- }
8724
- await this.processMessageLoop(null, ui, depth + 1);
8725
- return;
8726
- }
8727
- }
8728
8776
  if (serverTxStoredFromStream > 0) {
8729
8777
  if (this.config.verbose)
8730
8778
  process.stderr.write(
@@ -9280,7 +9328,8 @@ async function executeAgent(ctx2, options) {
9280
9328
  viaAgent: options.viaAgent,
9281
9329
  sessionId: options.sessionId,
9282
9330
  verbose: options.verbose,
9283
- notificationUrl: options.notificationUrl || process.env.VULTISIG_NOTIFICATION_URL || ""
9331
+ notificationUrl: options.notificationUrl || process.env.VULTISIG_NOTIFICATION_URL || "",
9332
+ profile: options.profile ?? process.env.VULTISIG_AGENT_PROFILE ?? ""
9284
9333
  };
9285
9334
  const session = new AgentSession(vault, config);
9286
9335
  if (options.viaAgent) {
@@ -9323,7 +9372,8 @@ async function executeAgentAsk(ctx2, message, options) {
9323
9372
  password: options.password,
9324
9373
  sessionId: options.session,
9325
9374
  verbose: options.verbose,
9326
- askMode: true
9375
+ askMode: true,
9376
+ profile: options.profile ?? process.env.VULTISIG_AGENT_PROFILE ?? ""
9327
9377
  };
9328
9378
  const session = new AgentSession(vault, config);
9329
9379
  const ask = new AskInterface(session, !!config.verbose);
@@ -9453,7 +9503,7 @@ var cachedVersion = null;
9453
9503
  function getVersion() {
9454
9504
  if (cachedVersion) return cachedVersion;
9455
9505
  if (true) {
9456
- cachedVersion = "0.22.0";
9506
+ cachedVersion = "0.22.4";
9457
9507
  return cachedVersion;
9458
9508
  }
9459
9509
  try {
@@ -10071,7 +10121,7 @@ var EventBuffer = class {
10071
10121
  };
10072
10122
 
10073
10123
  // src/interactive/session.ts
10074
- import { fiatCurrencies as fiatCurrencies4 } from "@vultisig/sdk";
10124
+ import { fiatCurrencies as fiatCurrencies3 } from "@vultisig/sdk";
10075
10125
  import chalk13 from "chalk";
10076
10126
  import ora3 from "ora";
10077
10127
  import * as readline3 from "readline";
@@ -10889,9 +10939,9 @@ Error: ${error2.message}`));
10889
10939
  i++;
10890
10940
  }
10891
10941
  }
10892
- if (!fiatCurrencies4.includes(currency)) {
10942
+ if (!fiatCurrencies3.includes(currency)) {
10893
10943
  console.log(chalk13.red(`Invalid currency: ${currency}`));
10894
- console.log(chalk13.yellow(`Supported currencies: ${fiatCurrencies4.join(", ")}`));
10944
+ console.log(chalk13.yellow(`Supported currencies: ${fiatCurrencies3.join(", ")}`));
10895
10945
  return;
10896
10946
  }
10897
10947
  const raw = args.includes("--raw");
@@ -11534,6 +11584,10 @@ async function init(vaultOverride, unlockPassword, passwordTTL) {
11534
11584
  if (vault) {
11535
11585
  await ctx.setActiveVault(vault);
11536
11586
  setupVaultEvents(vault);
11587
+ if (unlockPassword) {
11588
+ cachePassword(vault.id, unlockPassword);
11589
+ if (vault.name) cachePassword(vault.name, unlockPassword);
11590
+ }
11537
11591
  }
11538
11592
  }
11539
11593
  return ctx;
@@ -12143,7 +12197,7 @@ rujiraCmd.command("withdraw <asset> <amount> <l1Address>").description("Withdraw
12143
12197
  }
12144
12198
  )
12145
12199
  );
12146
- var agentCmd = program.command("agent").description("AI-powered chat interface for wallet operations").option("--via-agent", "Use NDJSON pipe mode for agent-to-agent communication").option("--verbose", "Show detailed tool call parameters and debug output").option("--backend-url <url>", "Agent backend URL (default: https://abe.vultisig.com)").option("--password <password>", "Vault password for signing operations").option("--password-ttl <ms>", "Password cache TTL in milliseconds (default: 300000, 86400000/24h for --via-agent)").option("--session-id <id>", "Resume an existing session").option("--notification-url <url>", "Notification service URL for push notifications").action(
12200
+ var agentCmd = program.command("agent").description("AI-powered chat interface for wallet operations").option("--via-agent", "Use NDJSON pipe mode for agent-to-agent communication").option("--verbose", "Show detailed tool call parameters and debug output").option("--backend-url <url>", "Agent backend URL (default: https://abe.vultisig.com)").option("--password <password>", "Vault password for signing operations").option("--password-ttl <ms>", "Password cache TTL in milliseconds (default: 300000, 86400000/24h for --via-agent)").option("--session-id <id>", "Resume an existing session").option("--notification-url <url>", "Notification service URL for push notifications").option("--profile <api_id>", "Billing profile slug sent as X-Vultisig-Abe-Profile header").action(
12147
12201
  async (options) => {
12148
12202
  const MAX_TTL = 864e5;
12149
12203
  let passwordTTL;
@@ -12165,16 +12219,18 @@ var agentCmd = program.command("agent").description("AI-powered chat interface f
12165
12219
  backendUrl: options.backendUrl,
12166
12220
  password: options.password,
12167
12221
  sessionId: options.sessionId,
12168
- notificationUrl: options.notificationUrl
12222
+ notificationUrl: options.notificationUrl,
12223
+ profile: options.profile
12169
12224
  });
12170
12225
  }
12171
12226
  );
12172
- agentCmd.command("ask <message>").description("Send a single message and get the response (for AI agent integration)").option("--session <id>", "Continue an existing conversation").option("--backend-url <url>", "Agent backend URL (default: https://abe.vultisig.com)").option("--password <password>", "Vault password for signing operations").option("--verbose", "Show tool calls and debug info on stderr").option("--json", "Output structured JSON (deprecated: use --output json)").addHelpText(
12227
+ agentCmd.command("ask <message>").description("Send a single message and get the response (for AI agent integration)").option("--session <id>", "Continue an existing conversation").option("--backend-url <url>", "Agent backend URL (default: https://abe.vultisig.com)").option("--password <password>", "Vault password for signing operations").option("--verbose", "Show tool calls and debug info on stderr").option("--json", "Output structured JSON (deprecated: use --output json)").option("--profile <api_id>", "Billing profile slug sent as X-Vultisig-Abe-Profile header").addHelpText(
12173
12228
  "after",
12174
12229
  `
12175
12230
  Examples:
12176
12231
  vultisig agent ask "What is my ETH balance?" --output json
12177
- vultisig agent ask "Send 0.1 ETH to 0x..." --session abc123 --yes`
12232
+ vultisig agent ask "Send 0.1 ETH to 0x..." --session abc123
12233
+ vultisig agent ask "..." --profile station-wallet`
12178
12234
  ).action(
12179
12235
  async (message, options) => {
12180
12236
  const parentOpts = agentCmd.opts();
@@ -12183,7 +12239,8 @@ Examples:
12183
12239
  ...options,
12184
12240
  backendUrl: options.backendUrl || parentOpts.backendUrl,
12185
12241
  password: options.password || parentOpts.password,
12186
- verbose: options.verbose || parentOpts.verbose
12242
+ verbose: options.verbose || parentOpts.verbose,
12243
+ profile: options.profile ?? parentOpts.profile
12187
12244
  });
12188
12245
  }
12189
12246
  );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vultisig/cli",
3
- "version": "0.22.0",
3
+ "version": "0.22.4",
4
4
  "description": "The self-custody MPC wallet CLI for AI coding agents (Claude Code, Cursor, OpenCode). Natural-language agent mode, 36+ chains, DKLS23 threshold signatures. Seedless.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -71,12 +71,12 @@
71
71
  "@cosmjs/encoding": "^0.38.1",
72
72
  "@cosmjs/proto-signing": "^0.38.1",
73
73
  "@cosmjs/stargate": "^0.38.1",
74
- "@napi-rs/keyring": "^1.2.0",
74
+ "@napi-rs/keyring": "^1.3.0",
75
75
  "@noble/hashes": "^2.0.1",
76
76
  "@vultisig/client-shared": "^0.2.6",
77
- "@vultisig/core-chain": "^1.4.2",
78
- "@vultisig/rujira": "^17.0.0",
79
- "@vultisig/sdk": "^0.22.0",
77
+ "@vultisig/core-chain": "^1.6.0",
78
+ "@vultisig/rujira": "^17.0.1",
79
+ "@vultisig/sdk": "^0.22.3",
80
80
  "chalk": "^5.6.2",
81
81
  "cli-table3": "^0.6.5",
82
82
  "commander": "^14.0.3",
@@ -94,7 +94,7 @@
94
94
  "@types/ws": "^8.18.1",
95
95
  "esbuild": "^0.27.4",
96
96
  "tsx": "^4.21.0",
97
- "typescript": "^5.9.3",
97
+ "typescript": "^6.0.3",
98
98
  "vitest": "^4.1.5"
99
99
  },
100
100
  "engines": {