@vultisig/cli 2.8.1 → 2.8.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 +54 -0
  2. package/dist/index.js +144 -51
  3. package/package.json +4 -4
package/CHANGELOG.md CHANGED
@@ -1,5 +1,59 @@
1
1
  # @vultisig/cli
2
2
 
3
+ ## 2.8.4
4
+
5
+ ### Patch Changes
6
+
7
+ - [#879](https://github.com/vultisig/vultisig-sdk/pull/879) [`f34ef93`](https://github.com/vultisig/vultisig-sdk/commit/f34ef932ba18c19accccc716098c3c16b83da625) Thanks [@neavra](https://github.com/neavra)! - Fix `agent ask` error reporting so a real first-turn backend/stream error is no
8
+ longer masked by the initialize-time `SESSION_NOT_FOUND` signal. The
9
+ stale-`--session` fallback signal is now kept separately and used only as a
10
+ lowest-priority fallback when the turn produced no error of its own, so a
11
+ genuine turn error wins while a clean turn after a stale-session fallback still
12
+ reports `SESSION_NOT_FOUND` (non-zero exit).
13
+
14
+ - [#875](https://github.com/vultisig/vultisig-sdk/pull/875) [`a1d711b`](https://github.com/vultisig/vultisig-sdk/commit/a1d711beaf1694883688cd8e430c8b38162975b6) Thanks [@neavra](https://github.com/neavra)! - agent: surface a loop-depth overrun as a typed `LOOP_DEPTH_EXCEEDED` error instead of a success-shaped `done`.
15
+
16
+ When the agent message loop exceeds `MAX_MESSAGE_LOOP_DEPTH` (16), `processMessageLoop` previously cleared the queued tool results and called `ui.onDone()` — the same callback used on a clean finish — so a headless caller (`agent ask --json` / `--via-agent` pipe) could not distinguish a depth-capped truncation from a completed turn. It now emits `AgentErrorCode.LOOP_DEPTH_EXCEEDED` via `ui.onError` first (ask exits non-zero with an error envelope; pipe gets a typed `error` frame), then `onDone()` as the turn terminator.
17
+
18
+ - Updated dependencies [[`26dd218`](https://github.com/vultisig/vultisig-sdk/commit/26dd218282b198fdea9c1118e7fe5bc800c071fb), [`69bb830`](https://github.com/vultisig/vultisig-sdk/commit/69bb8307de72883f0c7693871a6ca040b7a0756c)]:
19
+ - @vultisig/client-shared@0.2.17
20
+ - @vultisig/core-chain@2.17.10
21
+ - @vultisig/sdk@2.8.4
22
+
23
+ ## 2.8.2
24
+
25
+ ### Patch Changes
26
+
27
+ - [#869](https://github.com/vultisig/vultisig-sdk/pull/869) [`2623ca0`](https://github.com/vultisig/vultisig-sdk/commit/2623ca0dd04c5ca3c65126b6008a93c1d7559793) Thanks [@neavra](https://github.com/neavra)! - Harden agent conversation auth across every path. A revoked-but-unexpired
28
+ cached token now recovers uniformly: a new `withAuthRetry` helper does
29
+ clear→reauth→retry-once and wraps the fresh-conversation `createConversation`
30
+ (previously unguarded — it hard-threw `Authentication failed`), the resume
31
+ `getConversation`, the send-message stream, and the `agent sessions
32
+ list`/`delete` commands.
33
+
34
+ A `--session-id` resume whose 401 survives the single retry — or that fails for
35
+ any other reason — now falls back to a fresh conversation instead of throwing
36
+ uncaught, and emits a typed, non-fatal `SESSION_NOT_FOUND` signal carrying the
37
+ new conversation id so a headless caller knows prior context was dropped. In
38
+ `agent ask`, that initialize-time signal is now preserved into the first turn's
39
+ result envelope (it was previously cleared at turn start, so a stale-session
40
+ `ask --json` returned a false-success envelope instead of the promised non-zero
41
+ `SESSION_NOT_FOUND`).
42
+
43
+ Models the backend's `refresh_token`/`access_token` in `AuthTokenResponse` and
44
+ persists the refresh token in the token cache (0o600) for a future
45
+ `POST /auth/refresh` exchange; the retry path re-auths via MPC re-sign today.
46
+ The retry preserves a previously cached refresh token when the re-auth response
47
+ omits one, and the cache directory is chmod'd to 0o700 on every write (not only
48
+ on create) so a pre-existing `~/.vultisig` can't retain looser perms on upgrade.
49
+
50
+ Adds the first `auth.ts` unit tests (EIP-191 hash cross-checked against viem,
51
+ DER→65-byte formatting, signing-retry classification) plus session
52
+ auth-retry/fallback coverage.
53
+
54
+ - Updated dependencies [[`0dc1620`](https://github.com/vultisig/vultisig-sdk/commit/0dc16206bedcdde8832a068b15383565a6b98896)]:
55
+ - @vultisig/sdk@2.8.2
56
+
3
57
  ## 2.8.1
4
58
 
5
59
  ### Patch Changes
package/dist/index.js CHANGED
@@ -8074,6 +8074,8 @@ var AgentErrorCode = /* @__PURE__ */ ((AgentErrorCode3) => {
8074
8074
  AgentErrorCode3["TRANSACTION_FAILED"] = "TRANSACTION_FAILED";
8075
8075
  AgentErrorCode3["SIGNING_FAILED"] = "SIGNING_FAILED";
8076
8076
  AgentErrorCode3["SESSION_NOT_INITIALIZED"] = "SESSION_NOT_INITIALIZED";
8077
+ AgentErrorCode3["LOOP_DEPTH_EXCEEDED"] = "LOOP_DEPTH_EXCEEDED";
8078
+ AgentErrorCode3["SESSION_NOT_FOUND"] = "SESSION_NOT_FOUND";
8077
8079
  AgentErrorCode3["UNKNOWN_ERROR"] = "UNKNOWN_ERROR";
8078
8080
  return AgentErrorCode3;
8079
8081
  })(AgentErrorCode || {});
@@ -8081,6 +8083,10 @@ var AGENT_ERROR_CODE_VALUES = new Set(Object.values(AgentErrorCode));
8081
8083
  function isAgentErrorCode(value) {
8082
8084
  return AGENT_ERROR_CODE_VALUES.has(value);
8083
8085
  }
8086
+ var TERMINAL_AGENT_ERROR_CODES = /* @__PURE__ */ new Set(["LOOP_DEPTH_EXCEEDED" /* LOOP_DEPTH_EXCEEDED */]);
8087
+ function isTerminalAgentErrorCode(code) {
8088
+ return TERMINAL_AGENT_ERROR_CODES.has(code);
8089
+ }
8084
8090
  function mapVaultError(err) {
8085
8091
  if (err.code === VaultErrorCode2.InvalidConfig && /failed to unlock vault/i.test(err.message)) {
8086
8092
  return "AUTH_FAILED" /* AUTH_FAILED */;
@@ -8185,6 +8191,22 @@ var AskInterface = class {
8185
8191
  transactions = [];
8186
8192
  cards = [];
8187
8193
  error;
8194
+ // Initialize-time error, kept SEPARATE from the turn error so it stays the
8195
+ // LOWEST-priority signal. initialize() drives getCallbacks() BEFORE the first
8196
+ // ask() — a stale --session fallback fires onError(SESSION_NOT_FOUND) there.
8197
+ // A real first-turn error must override it, so we never pre-set `this.error`
8198
+ // with the init signal; instead partialResult() falls back to `initError` only
8199
+ // when the turn produced no error of its own. Cleared after the first turn so
8200
+ // later turns don't carry the init signal.
8201
+ initError;
8202
+ // Tracks whether the currently-latched `error` is a terminal one (e.g. the
8203
+ // depth cap). A terminal error may overwrite a prior non-terminal one; once a
8204
+ // terminal error is recorded, later frames cannot replace it. See onError.
8205
+ errorIsTerminal = false;
8206
+ // Whether ask() has run at least once. Distinguishes init-time onError (sets
8207
+ // initError) from turn onError (sets error), and gates clearing initError so
8208
+ // the init signal only carries into the FIRST turn.
8209
+ hasAsked = false;
8188
8210
  constructor(session, verbose = false, autoApprove = false) {
8189
8211
  this.session = session;
8190
8212
  this.verbose = verbose;
@@ -8237,8 +8259,12 @@ var AskInterface = class {
8237
8259
  }
8238
8260
  },
8239
8261
  onError: (message, code) => {
8240
- if (!this.error) {
8262
+ const isTerminal = isTerminalAgentErrorCode(code);
8263
+ if (!this.hasAsked) {
8264
+ this.initError = { message, code };
8265
+ } else if (!this.error || isTerminal && !this.errorIsTerminal) {
8241
8266
  this.error = { message, code };
8267
+ this.errorIsTerminal = isTerminal;
8242
8268
  }
8243
8269
  process.stderr.write(`[error] ${message} [${code}]
8244
8270
  `);
@@ -8270,6 +8296,11 @@ var AskInterface = class {
8270
8296
  this.transactions = [];
8271
8297
  this.cards = [];
8272
8298
  this.error = void 0;
8299
+ if (this.hasAsked) {
8300
+ this.initError = void 0;
8301
+ this.errorIsTerminal = false;
8302
+ }
8303
+ this.hasAsked = true;
8273
8304
  const callbacks = this.getCallbacks();
8274
8305
  await this.session.sendMessage(message, callbacks);
8275
8306
  return this.partialResult();
@@ -8289,7 +8320,9 @@ var AskInterface = class {
8289
8320
  toolCalls: this.toolCalls,
8290
8321
  transactions: this.transactions,
8291
8322
  cards: this.cards,
8292
- error: this.error
8323
+ // A real turn error wins; fall back to the init-time signal (e.g. stale
8324
+ // --session SESSION_NOT_FOUND) only when the turn produced no error.
8325
+ error: this.error ?? this.initError
8293
8326
  };
8294
8327
  }
8295
8328
  };
@@ -8624,7 +8657,13 @@ async function authenticateVault(client, vault, password, maxAttempts = 3) {
8624
8657
  });
8625
8658
  return {
8626
8659
  token: authResponse.token,
8627
- expiresAt: authResponse.expires_at
8660
+ expiresAt: authResponse.expires_at,
8661
+ // Captured + persisted by the session token cache. The backend exposes
8662
+ // POST /auth/refresh to exchange it for a fresh access token without a
8663
+ // new MPC round; wiring that exchange is a future enhancement — today
8664
+ // the CLI re-auths via a full MPC re-sign (authenticateVault), which is
8665
+ // always available and avoids depending on refresh-token rotation.
8666
+ refreshToken: authResponse.refresh_token
8628
8667
  };
8629
8668
  } catch (err) {
8630
8669
  lastError = err;
@@ -12072,7 +12111,7 @@ var PipeInterface = class {
12072
12111
  };
12073
12112
 
12074
12113
  // src/agent/session.ts
12075
- import { existsSync, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "node:fs";
12114
+ import { chmodSync, existsSync, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "node:fs";
12076
12115
  import { homedir as homedir2 } from "node:os";
12077
12116
  import { join as join2 } from "node:path";
12078
12117
  import { MemoryStorage, PushNotificationService } from "@vultisig/sdk";
@@ -12144,7 +12183,7 @@ var AgentSession = class {
12144
12183
  } else {
12145
12184
  const auth = await authenticateVault(this.client, this.vault, this.config.password);
12146
12185
  this.client.setAuthToken(auth.token);
12147
- saveCachedToken(this.publicKey, auth.token, auth.expiresAt);
12186
+ saveCachedToken(this.publicKey, auth.token, auth.expiresAt, auth.refreshToken);
12148
12187
  }
12149
12188
  } catch (err) {
12150
12189
  throw new Error(`Authentication failed: ${err.message}`);
@@ -12152,25 +12191,20 @@ var AgentSession = class {
12152
12191
  if (this.config.sessionId) {
12153
12192
  this.conversationId = this.config.sessionId;
12154
12193
  try {
12155
- const conv = await this.client.getConversation(this.conversationId, this.publicKey);
12194
+ const conv = await this.withAuthRetry(() => this.client.getConversation(this.conversationId, this.publicKey));
12156
12195
  this.historyMessages = conv.messages || [];
12157
12196
  } catch (err) {
12158
- if (err.message?.includes("401") || err.message?.includes("403")) {
12159
- clearCachedToken(this.publicKey);
12160
- const auth = await authenticateVault(this.client, this.vault, this.config.password);
12161
- this.client.setAuthToken(auth.token);
12162
- saveCachedToken(this.publicKey, auth.token, auth.expiresAt);
12163
- const conv = await this.client.getConversation(this.conversationId, this.publicKey);
12164
- this.historyMessages = conv.messages || [];
12165
- } else {
12166
- this.conversationId = null;
12167
- this.historyMessages = [];
12168
- const conv = await this.client.createConversation(this.publicKey);
12169
- this.conversationId = conv.id;
12170
- }
12197
+ this.conversationId = null;
12198
+ this.historyMessages = [];
12199
+ const conv = await this.withAuthRetry(() => this.client.createConversation(this.publicKey));
12200
+ this.conversationId = conv.id;
12201
+ ui.onError(
12202
+ `Session ${this.config.sessionId} could not be resumed (${err?.message ?? "unknown error"}); started a new conversation ${conv.id}`,
12203
+ "SESSION_NOT_FOUND" /* SESSION_NOT_FOUND */
12204
+ );
12171
12205
  }
12172
12206
  } else {
12173
- const conv = await this.client.createConversation(this.publicKey);
12207
+ const conv = await this.withAuthRetry(() => this.client.createConversation(this.publicKey));
12174
12208
  this.conversationId = conv.id;
12175
12209
  }
12176
12210
  this.cachedContext = this.config.viaAgent || this.config.askMode ? await buildMinimalContext(this.vault) : await buildMessageContext(this.vault);
@@ -12204,6 +12238,40 @@ var AgentSession = class {
12204
12238
  }
12205
12239
  }
12206
12240
  }
12241
+ /**
12242
+ * Run an authenticated backend request and, on a 401/403, do a single
12243
+ * clear → re-auth → retry. This is the ONE chokepoint every conversation
12244
+ * request shares (resume fetch, fresh-convo create, error-fallback create,
12245
+ * and the send-message stream) so a revoked-but-unexpired cached token
12246
+ * recovers identically everywhere instead of throwing on some paths.
12247
+ *
12248
+ * The retry replays the EXACT same `request` closure, which matters for the
12249
+ * send-message path: the replayed body must carry the same content +
12250
+ * recent_actions or the LLM re-emits tool calls (runaway loop). Re-auth is a
12251
+ * full MPC re-sign via authenticateVault — the backend also exposes
12252
+ * POST /auth/refresh, but exchanging the refresh token is a future
12253
+ * enhancement (see auth.ts); the re-sign is always available.
12254
+ *
12255
+ * `onReauth` (optional) fires the instant a re-auth is committed to — BEFORE
12256
+ * authenticateVault runs — so a caller in a retry loop (recoverDisconnectedTurn)
12257
+ * can record that its single re-auth has been spent even if the MPC re-sign
12258
+ * itself then throws. Without this hook a re-auth that fails with a non-auth
12259
+ * error would let the caller re-enter and re-sign on every iteration.
12260
+ */
12261
+ async withAuthRetry(request, onReauth) {
12262
+ try {
12263
+ return await request();
12264
+ } catch (err) {
12265
+ if (!isAuthError(err)) throw err;
12266
+ onReauth?.();
12267
+ const previousRefreshToken = readTokenStore()[this.publicKey]?.refreshToken;
12268
+ clearCachedToken(this.publicKey);
12269
+ const auth = await authenticateVault(this.client, this.vault, this.config.password);
12270
+ this.client.setAuthToken(auth.token);
12271
+ saveCachedToken(this.publicKey, auth.token, auth.expiresAt, auth.refreshToken ?? previousRefreshToken);
12272
+ return await request();
12273
+ }
12274
+ }
12207
12275
  getConversationId() {
12208
12276
  return this.conversationId;
12209
12277
  }
@@ -12253,6 +12321,10 @@ var AgentSession = class {
12253
12321
  `
12254
12322
  );
12255
12323
  this.pendingToolResults = [];
12324
+ ui.onError(
12325
+ `agent message loop exceeded ${MAX_MESSAGE_LOOP_DEPTH} turns; conversation truncated`,
12326
+ "LOOP_DEPTH_EXCEEDED" /* LOOP_DEPTH_EXCEEDED */
12327
+ );
12256
12328
  ui.onDone();
12257
12329
  return;
12258
12330
  }
@@ -12328,31 +12400,15 @@ var AgentSession = class {
12328
12400
  }
12329
12401
  };
12330
12402
  let streamResult;
12331
- let authRetried = false;
12332
- while (true) {
12333
- try {
12334
- streamResult = await this.client.sendMessageStream(
12335
- this.conversationId,
12336
- request,
12337
- callbacks,
12338
- this.abortController?.signal
12339
- );
12340
- break;
12341
- } catch (err) {
12342
- const isAuthErr = err.message?.includes("401") || err.message?.includes("403");
12343
- if (isAuthErr && !authRetried) {
12344
- authRetried = true;
12345
- clearCachedToken(this.publicKey);
12346
- const auth = await authenticateVault(this.client, this.vault, this.config.password);
12347
- this.client.setAuthToken(auth.token);
12348
- saveCachedToken(this.publicKey, auth.token, auth.expiresAt);
12349
- continue;
12350
- }
12351
- if (flushedThisCall.length > 0) {
12352
- this.pendingToolResults = [...flushedThisCall, ...this.pendingToolResults];
12353
- }
12354
- throw err;
12403
+ try {
12404
+ streamResult = await this.withAuthRetry(
12405
+ () => this.client.sendMessageStream(this.conversationId, request, callbacks, this.abortController?.signal)
12406
+ );
12407
+ } catch (err) {
12408
+ if (flushedThisCall.length > 0) {
12409
+ this.pendingToolResults = [...flushedThisCall, ...this.pendingToolResults];
12355
12410
  }
12411
+ throw err;
12356
12412
  }
12357
12413
  if (pendingDispatches.length > 0) {
12358
12414
  await Promise.all(pendingDispatches);
@@ -12415,10 +12471,16 @@ var AgentSession = class {
12415
12471
  const since = serverAnchor ?? new Date(Date.now() - 2e3).toISOString();
12416
12472
  const replaySignableCards = serverAnchor !== null;
12417
12473
  let cursor;
12474
+ let authRecovered = false;
12418
12475
  for (let attempt = 0; attempt < this.recoveryMaxPolls; attempt++) {
12419
12476
  let resp;
12420
12477
  try {
12421
- resp = await this.client.messagesSince(this.conversationId, cursor ? { cursor } : { since });
12478
+ resp = authRecovered ? await this.client.messagesSince(this.conversationId, cursor ? { cursor } : { since }) : await this.withAuthRetry(
12479
+ () => this.client.messagesSince(this.conversationId, cursor ? { cursor } : { since }),
12480
+ () => {
12481
+ authRecovered = true;
12482
+ }
12483
+ );
12422
12484
  } catch (err) {
12423
12485
  if (this.config.verbose) {
12424
12486
  process.stderr.write(`[session] recovery poll ${attempt + 1} failed: ${err?.message ?? err}
@@ -12708,6 +12770,10 @@ function serverNowToIso(serverNow) {
12708
12770
  function hasTxReadyPart(parts) {
12709
12771
  return !!parts?.some((p) => p.type === "data-tx_ready" && !!p.data);
12710
12772
  }
12773
+ function isAuthError(err) {
12774
+ const msg = err instanceof Error ? err.message : String(err ?? "");
12775
+ return /\b(401|403)\b/.test(msg);
12776
+ }
12711
12777
  function getTokenCachePath() {
12712
12778
  const dir = process.env.VULTISIG_CONFIG_DIR ?? join2(homedir2(), ".vultisig");
12713
12779
  return join2(dir, "agent-tokens.json");
@@ -12724,8 +12790,16 @@ function readTokenStore() {
12724
12790
  function writeTokenStore(store) {
12725
12791
  const path4 = getTokenCachePath();
12726
12792
  const dir = join2(path4, "..");
12727
- if (!existsSync(dir)) mkdirSync2(dir, { recursive: true });
12793
+ if (!existsSync(dir)) mkdirSync2(dir, { recursive: true, mode: 448 });
12794
+ try {
12795
+ chmodSync(dir, 448);
12796
+ } catch {
12797
+ }
12728
12798
  writeFileSync2(path4, JSON.stringify(store, null, 2), { mode: 384 });
12799
+ try {
12800
+ chmodSync(path4, 384);
12801
+ } catch {
12802
+ }
12729
12803
  }
12730
12804
  function loadCachedToken(publicKey) {
12731
12805
  const store = readTokenStore();
@@ -12743,9 +12817,13 @@ function loadCachedToken(publicKey) {
12743
12817
  }
12744
12818
  return entry.token;
12745
12819
  }
12746
- function saveCachedToken(publicKey, token, expiresAt) {
12820
+ function saveCachedToken(publicKey, token, expiresAt, refreshToken) {
12747
12821
  const store = readTokenStore();
12748
- store[publicKey] = { token, expiresAt };
12822
+ store[publicKey] = {
12823
+ token,
12824
+ expiresAt,
12825
+ refreshToken: refreshToken ?? store[publicKey]?.refreshToken
12826
+ };
12749
12827
  try {
12750
12828
  writeTokenStore(store);
12751
12829
  } catch {
@@ -13293,7 +13371,12 @@ async function executeAgentSessionsList(ctx2, options) {
13293
13371
  let totalCount = 0;
13294
13372
  let skip = 0;
13295
13373
  while (true) {
13296
- const page = await client.listConversations(publicKey, skip, PAGE_SIZE);
13374
+ const page = await withClientAuthRetry(
13375
+ client,
13376
+ vault,
13377
+ options.password,
13378
+ () => client.listConversations(publicKey, skip, PAGE_SIZE)
13379
+ );
13297
13380
  totalCount = page.total_count;
13298
13381
  allConversations.push(...page.conversations);
13299
13382
  if (allConversations.length >= totalCount || page.conversations.length < PAGE_SIZE) break;
@@ -13335,7 +13418,7 @@ async function executeAgentSessionsDelete(ctx2, sessionId, options) {
13335
13418
  const backendUrl = options.backendUrl || process.env.VULTISIG_AGENT_URL || "https://abe.vultisig.com";
13336
13419
  const client = await createAuthenticatedClient(backendUrl, vault, options.password);
13337
13420
  const publicKey = vault.publicKeys.ecdsa;
13338
- await client.deleteConversation(sessionId, publicKey);
13421
+ await withClientAuthRetry(client, vault, options.password, () => client.deleteConversation(sessionId, publicKey));
13339
13422
  if (isJsonOutput()) {
13340
13423
  outputJson({ deleted: sessionId });
13341
13424
  return;
@@ -13348,6 +13431,16 @@ async function createAuthenticatedClient(backendUrl, vault, password) {
13348
13431
  client.setAuthToken(auth.token);
13349
13432
  return client;
13350
13433
  }
13434
+ async function withClientAuthRetry(client, vault, password, request) {
13435
+ try {
13436
+ return await request();
13437
+ } catch (err) {
13438
+ if (!isAuthError(err)) throw err;
13439
+ const auth = await authenticateVault(client, vault, password);
13440
+ client.setAuthToken(auth.token);
13441
+ return await request();
13442
+ }
13443
+ }
13351
13444
  function formatDate(iso) {
13352
13445
  try {
13353
13446
  const d = new Date(iso);
@@ -13366,7 +13459,7 @@ var cachedVersion = null;
13366
13459
  function getVersion() {
13367
13460
  if (cachedVersion) return cachedVersion;
13368
13461
  if (true) {
13369
- cachedVersion = "2.8.1";
13462
+ cachedVersion = "2.8.4";
13370
13463
  return cachedVersion;
13371
13464
  }
13372
13465
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vultisig/cli",
3
- "version": "2.8.1",
3
+ "version": "2.8.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": {
@@ -73,10 +73,10 @@
73
73
  "@cosmjs/stargate": "^0.39.0",
74
74
  "@napi-rs/keyring": "^1.3.0",
75
75
  "@noble/hashes": "^2.2.0",
76
- "@vultisig/client-shared": "^0.2.16",
77
- "@vultisig/core-chain": "^2.17.8",
76
+ "@vultisig/client-shared": "^0.2.17",
77
+ "@vultisig/core-chain": "^2.17.10",
78
78
  "@vultisig/rujira": "^41.0.0",
79
- "@vultisig/sdk": "^2.8.0",
79
+ "@vultisig/sdk": "^2.8.4",
80
80
  "chalk": "^5.6.2",
81
81
  "cli-table3": "^0.6.5",
82
82
  "commander": "^15.0.0",