@vultisig/cli 1.8.1 → 2.0.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.
Files changed (3) hide show
  1. package/CHANGELOG.md +46 -0
  2. package/dist/index.js +229 -90
  3. package/package.json +16 -16
package/CHANGELOG.md CHANGED
@@ -1,5 +1,51 @@
1
1
  # @vultisig/cli
2
2
 
3
+ ## 2.0.0
4
+
5
+ ### Major Changes
6
+
7
+ - [#682](https://github.com/vultisig/vultisig-sdk/pull/682) [`341245e`](https://github.com/vultisig/vultisig-sdk/commit/341245e2d6d49348e38d61bc42805c35ac8d3052) Thanks [@neavra](https://github.com/neavra)! - Gate signing in `agent ask` mode behind explicit confirmation (security fix, **breaking**).
8
+
9
+ Previously `vsig agent ask` auto-signed and broadcast any transaction envelope the
10
+ backend returned, gated only by whether a password was present. Because the backend
11
+ routes read-only swap intents (e.g. "list swap routes from USDC to ETH") to the
12
+ fund-moving `execute_swap` tool, a query could broadcast a real on-chain swap.
13
+
14
+ `runPasswordGatedTool` now calls `ui.requestConfirmation` before any `sign_tx` /
15
+ `sign_typed_data` (the single chokepoint for both the tx_ready path and client-side
16
+ dispatch, covering both legs of a multi-leg swap). In ask mode this defaults to
17
+ **deny**: signing/broadcast now requires the new `agent ask --yes` flag. Without it,
18
+ the proposed transaction is reported (`CONFIRMATION_REQUIRED`) and nothing is signed.
19
+ Interactive (TUI) and pipe (`--via-agent`) modes already prompt/defer for confirmation.
20
+
21
+ **BREAKING — migration for unattended pipelines:** any automation that relied on
22
+ `agent ask` auto-signing must now pass `--yes`. A denied signing still exits **0**
23
+ (a misrouted read-only prompt remains a successful query); detect it via the new
24
+ top-level `confirmation_required: true` field in `--output json` mode, the
25
+ `confirmation-required:` line in text mode, or `tool_calls[].code ===
26
+ "CONFIRMATION_REQUIRED"`. Do not infer "broadcast happened" from exit code alone —
27
+ check the `transactions` array. With `--yes`, each authorization is logged to stderr
28
+ (`[confirm] auto-approved (--yes): <summary>`).
29
+
30
+ ### Patch Changes
31
+
32
+ - Updated dependencies [[`dc75595`](https://github.com/vultisig/vultisig-sdk/commit/dc75595e83360f5bda84b2d91cae177bc7c8c966)]:
33
+ - @vultisig/sdk@2.0.0
34
+ - @vultisig/rujira@33.0.0
35
+ - @vultisig/client-shared@0.2.15
36
+
37
+ ## 1.8.10
38
+
39
+ ### Patch Changes
40
+
41
+ - [#683](https://github.com/vultisig/vultisig-sdk/pull/683) [`4561129`](https://github.com/vultisig/vultisig-sdk/commit/45611297a55da72d3c56b1a2ffe6522da1b64d7b) Thanks [@rcoderdev](https://github.com/rcoderdev)! - Update SDK package dependencies and Yarn tooling.
42
+
43
+ - Updated dependencies [[`4561129`](https://github.com/vultisig/vultisig-sdk/commit/45611297a55da72d3c56b1a2ffe6522da1b64d7b)]:
44
+ - @vultisig/client-shared@0.2.14
45
+ - @vultisig/core-chain@2.14.1
46
+ - @vultisig/rujira@32.0.1
47
+ - @vultisig/sdk@1.8.10
48
+
3
49
  ## 1.8.1
4
50
 
5
51
  ### Patch Changes
package/dist/index.js CHANGED
@@ -1102,7 +1102,7 @@ var require_main = __commonJS({
1102
1102
  }
1103
1103
  });
1104
1104
 
1105
- // ../../node_modules/@noble/hashes/esm/_u64.js
1105
+ // node_modules/@noble/hashes/_u64.js
1106
1106
  function fromBig(n, le = false) {
1107
1107
  if (le)
1108
1108
  return { h: Number(n & U32_MASK64), l: Number(n >> _32n & U32_MASK64) };
@@ -1120,7 +1120,7 @@ function split(lst, le = false) {
1120
1120
  }
1121
1121
  var U32_MASK64, _32n, rotlSH, rotlSL, rotlBH, rotlBL;
1122
1122
  var init_u64 = __esm({
1123
- "../../node_modules/@noble/hashes/esm/_u64.js"() {
1123
+ "node_modules/@noble/hashes/_u64.js"() {
1124
1124
  U32_MASK64 = /* @__PURE__ */ BigInt(2 ** 32 - 1);
1125
1125
  _32n = /* @__PURE__ */ BigInt(32);
1126
1126
  rotlSH = (h, l, s) => h << s | l >>> 32 - s;
@@ -1130,19 +1130,34 @@ var init_u64 = __esm({
1130
1130
  }
1131
1131
  });
1132
1132
 
1133
- // ../../node_modules/@noble/hashes/esm/utils.js
1133
+ // node_modules/@noble/hashes/utils.js
1134
1134
  function isBytes(a) {
1135
- return a instanceof Uint8Array || ArrayBuffer.isView(a) && a.constructor.name === "Uint8Array";
1135
+ return a instanceof Uint8Array || ArrayBuffer.isView(a) && a.constructor.name === "Uint8Array" && "BYTES_PER_ELEMENT" in a && a.BYTES_PER_ELEMENT === 1;
1136
1136
  }
1137
- function anumber(n) {
1138
- if (!Number.isSafeInteger(n) || n < 0)
1139
- throw new Error("positive integer expected, got " + n);
1137
+ function anumber(n, title = "") {
1138
+ if (typeof n !== "number") {
1139
+ const prefix = title && `"${title}" `;
1140
+ throw new TypeError(`${prefix}expected number, got ${typeof n}`);
1141
+ }
1142
+ if (!Number.isSafeInteger(n) || n < 0) {
1143
+ const prefix = title && `"${title}" `;
1144
+ throw new RangeError(`${prefix}expected integer >= 0, got ${n}`);
1145
+ }
1140
1146
  }
1141
- function abytes(b, ...lengths) {
1142
- if (!isBytes(b))
1143
- throw new Error("Uint8Array expected");
1144
- if (lengths.length > 0 && !lengths.includes(b.length))
1145
- throw new Error("Uint8Array expected of length " + lengths + ", got length=" + b.length);
1147
+ function abytes(value, length, title = "") {
1148
+ const bytes = isBytes(value);
1149
+ const len = value?.length;
1150
+ const needsLen = length !== void 0;
1151
+ if (!bytes || needsLen && len !== length) {
1152
+ const prefix = title && `"${title}" `;
1153
+ const ofLen = needsLen ? ` of length ${length}` : "";
1154
+ const got = bytes ? `length=${len}` : `type=${typeof value}`;
1155
+ const message = prefix + "expected Uint8Array" + ofLen + ", got " + got;
1156
+ if (!bytes)
1157
+ throw new TypeError(message);
1158
+ throw new RangeError(message);
1159
+ }
1160
+ return value;
1146
1161
  }
1147
1162
  function aexists(instance, checkFinished = true) {
1148
1163
  if (instance.destroyed)
@@ -1151,10 +1166,10 @@ function aexists(instance, checkFinished = true) {
1151
1166
  throw new Error("Hash#digest() has already been called");
1152
1167
  }
1153
1168
  function aoutput(out, instance) {
1154
- abytes(out);
1169
+ abytes(out, void 0, "digestInto() output");
1155
1170
  const min = instance.outputLen;
1156
1171
  if (out.length < min) {
1157
- throw new Error("digestInto() expects output buffer of length at least " + min);
1172
+ throw new RangeError('"digestInto() output" expected to be of length >=' + min);
1158
1173
  }
1159
1174
  }
1160
1175
  function u32(arr) {
@@ -1174,44 +1189,30 @@ function byteSwap32(arr) {
1174
1189
  }
1175
1190
  return arr;
1176
1191
  }
1177
- function utf8ToBytes(str) {
1178
- if (typeof str !== "string")
1179
- throw new Error("string expected");
1180
- return new Uint8Array(new TextEncoder().encode(str));
1181
- }
1182
- function toBytes(data) {
1183
- if (typeof data === "string")
1184
- data = utf8ToBytes(data);
1185
- abytes(data);
1186
- return data;
1187
- }
1188
- function createHasher(hashCons) {
1189
- const hashC = (msg) => hashCons().update(toBytes(msg)).digest();
1190
- const tmp = hashCons();
1191
- hashC.outputLen = tmp.outputLen;
1192
- hashC.blockLen = tmp.blockLen;
1193
- hashC.create = () => hashCons();
1194
- return hashC;
1195
- }
1196
- function createXOFer(hashCons) {
1197
- const hashC = (msg, opts) => hashCons(opts).update(toBytes(msg)).digest();
1198
- const tmp = hashCons({});
1192
+ function createHasher(hashCons, info2 = {}) {
1193
+ const hashC = (msg, opts) => hashCons(opts).update(msg).digest();
1194
+ const tmp = hashCons(void 0);
1199
1195
  hashC.outputLen = tmp.outputLen;
1200
1196
  hashC.blockLen = tmp.blockLen;
1197
+ hashC.canXOF = tmp.canXOF;
1201
1198
  hashC.create = (opts) => hashCons(opts);
1202
- return hashC;
1199
+ Object.assign(hashC, info2);
1200
+ return Object.freeze(hashC);
1203
1201
  }
1204
- var isLE, swap32IfBE, Hash;
1202
+ var isLE, swap32IfBE, oidNist;
1205
1203
  var init_utils = __esm({
1206
- "../../node_modules/@noble/hashes/esm/utils.js"() {
1204
+ "node_modules/@noble/hashes/utils.js"() {
1207
1205
  isLE = /* @__PURE__ */ (() => new Uint8Array(new Uint32Array([287454020]).buffer)[0] === 68)();
1208
1206
  swap32IfBE = isLE ? (u) => u : byteSwap32;
1209
- Hash = class {
1210
- };
1207
+ oidNist = (suffix) => ({
1208
+ // Current NIST hashAlgs suffixes used here fit in one DER subidentifier octet.
1209
+ // Larger suffix values would need base-128 OID encoding and a different length byte.
1210
+ oid: Uint8Array.from([6, 9, 96, 134, 72, 1, 101, 3, 4, 2, suffix])
1211
+ });
1211
1212
  }
1212
1213
  });
1213
1214
 
1214
- // ../../node_modules/@noble/hashes/esm/sha3.js
1215
+ // node_modules/@noble/hashes/sha3.js
1215
1216
  var sha3_exports = {};
1216
1217
  __export(sha3_exports, {
1217
1218
  Keccak: () => Keccak,
@@ -1225,9 +1226,14 @@ __export(sha3_exports, {
1225
1226
  sha3_384: () => sha3_384,
1226
1227
  sha3_512: () => sha3_512,
1227
1228
  shake128: () => shake128,
1228
- shake256: () => shake256
1229
+ shake128_32: () => shake128_32,
1230
+ shake256: () => shake256,
1231
+ shake256_64: () => shake256_64
1229
1232
  });
1230
1233
  function keccakP(s, rounds = 24) {
1234
+ anumber(rounds, "rounds");
1235
+ if (rounds < 1 || rounds > 24)
1236
+ throw new Error('"rounds" expected integer 1..24');
1231
1237
  const B = new Uint32Array(5 * 2);
1232
1238
  for (let round = 24 - rounds; round < 24; round++) {
1233
1239
  for (let x = 0; x < 10; x++)
@@ -1257,19 +1263,26 @@ function keccakP(s, rounds = 24) {
1257
1263
  s[PI + 1] = Tl;
1258
1264
  }
1259
1265
  for (let y = 0; y < 50; y += 10) {
1260
- for (let x = 0; x < 10; x++)
1261
- B[x] = s[y + x];
1262
- for (let x = 0; x < 10; x++)
1263
- s[y + x] ^= ~B[(x + 2) % 10] & B[(x + 4) % 10];
1266
+ const b0 = s[y], b1 = s[y + 1], b2 = s[y + 2], b3 = s[y + 3];
1267
+ s[y] ^= ~s[y + 2] & s[y + 4];
1268
+ s[y + 1] ^= ~s[y + 3] & s[y + 5];
1269
+ s[y + 2] ^= ~s[y + 4] & s[y + 6];
1270
+ s[y + 3] ^= ~s[y + 5] & s[y + 7];
1271
+ s[y + 4] ^= ~s[y + 6] & s[y + 8];
1272
+ s[y + 5] ^= ~s[y + 7] & s[y + 9];
1273
+ s[y + 6] ^= ~s[y + 8] & b0;
1274
+ s[y + 7] ^= ~s[y + 9] & b1;
1275
+ s[y + 8] ^= ~b0 & b2;
1276
+ s[y + 9] ^= ~b1 & b3;
1264
1277
  }
1265
1278
  s[0] ^= SHA3_IOTA_H[round];
1266
1279
  s[1] ^= SHA3_IOTA_L[round];
1267
1280
  }
1268
1281
  clean(B);
1269
1282
  }
1270
- var _0n, _1n, _2n, _7n, _256n, _0x71n, SHA3_PI, SHA3_ROTL, _SHA3_IOTA, IOTAS, SHA3_IOTA_H, SHA3_IOTA_L, rotlH, rotlL, Keccak, gen, sha3_224, sha3_256, sha3_384, sha3_512, keccak_224, keccak_256, keccak_384, keccak_512, genShake, shake128, shake256;
1283
+ var _0n, _1n, _2n, _7n, _256n, _0x71n, SHA3_PI, SHA3_ROTL, _SHA3_IOTA, IOTAS, SHA3_IOTA_H, SHA3_IOTA_L, rotlH, rotlL, Keccak, genKeccak, sha3_224, sha3_256, sha3_384, sha3_512, keccak_224, keccak_256, keccak_384, keccak_512, genShake, shake128, shake256, shake128_32, shake256_64;
1271
1284
  var init_sha3 = __esm({
1272
- "../../node_modules/@noble/hashes/esm/sha3.js"() {
1285
+ "node_modules/@noble/hashes/sha3.js"() {
1273
1286
  init_u64();
1274
1287
  init_utils();
1275
1288
  _0n = BigInt(0);
@@ -1289,7 +1302,7 @@ var init_sha3 = __esm({
1289
1302
  for (let j = 0; j < 7; j++) {
1290
1303
  R = (R << _1n ^ (R >> _7n) * _0x71n) % _256n;
1291
1304
  if (R & _2n)
1292
- t ^= _1n << (_1n << /* @__PURE__ */ BigInt(j)) - _1n;
1305
+ t ^= _1n << (_1n << BigInt(j)) - _1n;
1293
1306
  }
1294
1307
  _SHA3_IOTA.push(t);
1295
1308
  }
@@ -1298,21 +1311,28 @@ var init_sha3 = __esm({
1298
1311
  SHA3_IOTA_L = IOTAS[1];
1299
1312
  rotlH = (h, l, s) => s > 32 ? rotlBH(h, l, s) : rotlSH(h, l, s);
1300
1313
  rotlL = (h, l, s) => s > 32 ? rotlBL(h, l, s) : rotlSL(h, l, s);
1301
- Keccak = class _Keccak extends Hash {
1314
+ Keccak = class _Keccak {
1315
+ state;
1316
+ pos = 0;
1317
+ posOut = 0;
1318
+ finished = false;
1319
+ state32;
1320
+ destroyed = false;
1321
+ blockLen;
1322
+ suffix;
1323
+ outputLen;
1324
+ canXOF;
1325
+ enableXOF = false;
1326
+ rounds;
1302
1327
  // NOTE: we accept arguments in bytes instead of bits here.
1303
1328
  constructor(blockLen, suffix, outputLen, enableXOF = false, rounds = 24) {
1304
- super();
1305
- this.pos = 0;
1306
- this.posOut = 0;
1307
- this.finished = false;
1308
- this.destroyed = false;
1309
- this.enableXOF = false;
1310
1329
  this.blockLen = blockLen;
1311
1330
  this.suffix = suffix;
1312
1331
  this.outputLen = outputLen;
1313
1332
  this.enableXOF = enableXOF;
1333
+ this.canXOF = enableXOF;
1314
1334
  this.rounds = rounds;
1315
- anumber(outputLen);
1335
+ anumber(outputLen, "outputLen");
1316
1336
  if (!(0 < blockLen && blockLen < 200))
1317
1337
  throw new Error("only keccak-f1600 function is supported");
1318
1338
  this.state = new Uint8Array(200);
@@ -1330,7 +1350,6 @@ var init_sha3 = __esm({
1330
1350
  }
1331
1351
  update(data) {
1332
1352
  aexists(this);
1333
- data = toBytes(data);
1334
1353
  abytes(data);
1335
1354
  const { blockLen, state } = this;
1336
1355
  const len = data.length;
@@ -1383,12 +1402,13 @@ var init_sha3 = __esm({
1383
1402
  aoutput(out, this);
1384
1403
  if (this.finished)
1385
1404
  throw new Error("digest() was already called");
1386
- this.writeInto(out);
1405
+ this.writeInto(out.subarray(0, this.outputLen));
1387
1406
  this.destroy();
1388
- return out;
1389
1407
  }
1390
1408
  digest() {
1391
- return this.digestInto(new Uint8Array(this.outputLen));
1409
+ const out = new Uint8Array(this.outputLen);
1410
+ this.digestInto(out);
1411
+ return out;
1392
1412
  }
1393
1413
  destroy() {
1394
1414
  this.destroyed = true;
@@ -1396,7 +1416,8 @@ var init_sha3 = __esm({
1396
1416
  }
1397
1417
  _cloneInto(to) {
1398
1418
  const { blockLen, suffix, outputLen, rounds, enableXOF } = this;
1399
- to || (to = new _Keccak(blockLen, suffix, outputLen, enableXOF, rounds));
1419
+ to ||= new _Keccak(blockLen, suffix, outputLen, enableXOF, rounds);
1420
+ to.blockLen = blockLen;
1400
1421
  to.state32.set(this.state32);
1401
1422
  to.pos = this.pos;
1402
1423
  to.posOut = this.posOut;
@@ -1405,22 +1426,45 @@ var init_sha3 = __esm({
1405
1426
  to.suffix = suffix;
1406
1427
  to.outputLen = outputLen;
1407
1428
  to.enableXOF = enableXOF;
1429
+ to.canXOF = this.canXOF;
1408
1430
  to.destroyed = this.destroyed;
1409
1431
  return to;
1410
1432
  }
1411
1433
  };
1412
- gen = (suffix, blockLen, outputLen) => createHasher(() => new Keccak(blockLen, suffix, outputLen));
1413
- sha3_224 = /* @__PURE__ */ (() => gen(6, 144, 224 / 8))();
1414
- sha3_256 = /* @__PURE__ */ (() => gen(6, 136, 256 / 8))();
1415
- sha3_384 = /* @__PURE__ */ (() => gen(6, 104, 384 / 8))();
1416
- sha3_512 = /* @__PURE__ */ (() => gen(6, 72, 512 / 8))();
1417
- keccak_224 = /* @__PURE__ */ (() => gen(1, 144, 224 / 8))();
1418
- keccak_256 = /* @__PURE__ */ (() => gen(1, 136, 256 / 8))();
1419
- keccak_384 = /* @__PURE__ */ (() => gen(1, 104, 384 / 8))();
1420
- keccak_512 = /* @__PURE__ */ (() => gen(1, 72, 512 / 8))();
1421
- genShake = (suffix, blockLen, outputLen) => createXOFer((opts = {}) => new Keccak(blockLen, suffix, opts.dkLen === void 0 ? outputLen : opts.dkLen, true));
1422
- shake128 = /* @__PURE__ */ (() => genShake(31, 168, 128 / 8))();
1423
- shake256 = /* @__PURE__ */ (() => genShake(31, 136, 256 / 8))();
1434
+ genKeccak = (suffix, blockLen, outputLen, info2 = {}) => createHasher(() => new Keccak(blockLen, suffix, outputLen), info2);
1435
+ sha3_224 = /* @__PURE__ */ genKeccak(
1436
+ 6,
1437
+ 144,
1438
+ 28,
1439
+ /* @__PURE__ */ oidNist(7)
1440
+ );
1441
+ sha3_256 = /* @__PURE__ */ genKeccak(
1442
+ 6,
1443
+ 136,
1444
+ 32,
1445
+ /* @__PURE__ */ oidNist(8)
1446
+ );
1447
+ sha3_384 = /* @__PURE__ */ genKeccak(
1448
+ 6,
1449
+ 104,
1450
+ 48,
1451
+ /* @__PURE__ */ oidNist(9)
1452
+ );
1453
+ sha3_512 = /* @__PURE__ */ genKeccak(
1454
+ 6,
1455
+ 72,
1456
+ 64,
1457
+ /* @__PURE__ */ oidNist(10)
1458
+ );
1459
+ keccak_224 = /* @__PURE__ */ genKeccak(1, 144, 28);
1460
+ keccak_256 = /* @__PURE__ */ genKeccak(1, 136, 32);
1461
+ keccak_384 = /* @__PURE__ */ genKeccak(1, 104, 48);
1462
+ keccak_512 = /* @__PURE__ */ genKeccak(1, 72, 64);
1463
+ genShake = (suffix, blockLen, outputLen, info2 = {}) => createHasher((opts = {}) => new Keccak(blockLen, suffix, opts.dkLen === void 0 ? outputLen : opts.dkLen, true), info2);
1464
+ shake128 = /* @__PURE__ */ genShake(31, 168, 16, /* @__PURE__ */ oidNist(11));
1465
+ shake256 = /* @__PURE__ */ genShake(31, 136, 32, /* @__PURE__ */ oidNist(12));
1466
+ shake128_32 = /* @__PURE__ */ genShake(31, 168, 32, /* @__PURE__ */ oidNist(11));
1467
+ shake256_64 = /* @__PURE__ */ genShake(31, 136, 64, /* @__PURE__ */ oidNist(12));
1424
1468
  }
1425
1469
  });
1426
1470
 
@@ -4753,12 +4797,14 @@ function normalizeAgentError(err) {
4753
4797
  var AskInterface = class {
4754
4798
  session;
4755
4799
  verbose;
4800
+ autoApprove;
4756
4801
  responseParts = [];
4757
4802
  toolCalls = [];
4758
4803
  transactions = [];
4759
- constructor(session, verbose = false) {
4804
+ constructor(session, verbose = false, autoApprove = false) {
4760
4805
  this.session = session;
4761
4806
  this.verbose = verbose;
4807
+ this.autoApprove = autoApprove;
4762
4808
  }
4763
4809
  /**
4764
4810
  * Get UI callbacks that silently collect results.
@@ -4806,8 +4852,15 @@ var AskInterface = class {
4806
4852
  requestPassword: async () => {
4807
4853
  throw new Error("Password required but not provided. Use --password flag.");
4808
4854
  },
4809
- requestConfirmation: async (_message) => {
4810
- return true;
4855
+ requestConfirmation: async (message) => {
4856
+ if (!this.autoApprove) {
4857
+ process.stderr.write(`[confirm] signing requires --yes \u2014 NOT broadcasting: ${message}
4858
+ `);
4859
+ } else {
4860
+ process.stderr.write(`[confirm] auto-approved (--yes): ${message}
4861
+ `);
4862
+ }
4863
+ return this.autoApprove;
4811
4864
  }
4812
4865
  };
4813
4866
  }
@@ -6148,6 +6201,7 @@ var AgentExecutor = class {
6148
6201
  const chain2 = resolveChainFromTxReady(txReadyData) || Chain10.Ethereum;
6149
6202
  if (getChainKind(chain2) !== "evm") {
6150
6203
  this.pendingPayloads.clear();
6204
+ this.pendingLegs = [];
6151
6205
  this.pendingPayloads.set("latest", {
6152
6206
  payload: { __serverTx: true, ...txReadyData },
6153
6207
  coin: { chain: chain2, address: "", decimals: 18, ticker: "" },
@@ -6171,6 +6225,7 @@ var AgentExecutor = class {
6171
6225
  }
6172
6226
  const chain = resolveChainFromTxReady(txReadyData) || Chain10.Ethereum;
6173
6227
  this.pendingPayloads.clear();
6228
+ this.pendingLegs = [];
6174
6229
  this.pendingPayloads.set("latest", {
6175
6230
  payload: { __serverTx: true, ...txReadyData },
6176
6231
  coin: { chain, address: "", decimals: 18, ticker: "" },
@@ -6187,6 +6242,43 @@ var AgentExecutor = class {
6187
6242
  hasPendingTransaction() {
6188
6243
  return this.pendingPayloads.has("latest");
6189
6244
  }
6245
+ /**
6246
+ * Drop the buffered server tx and any staged multi-leg state. Called when
6247
+ * the user declines the pre-sign confirmation: the rejected envelope must
6248
+ * not linger (a fresh tx_ready always overwrites, but stale legs/payloads
6249
+ * would otherwise survive into later turns).
6250
+ */
6251
+ clearPendingTransaction() {
6252
+ this.pendingPayloads.clear();
6253
+ this.pendingLegs = [];
6254
+ }
6255
+ /**
6256
+ * Human-readable one-line summary of the currently-buffered server tx
6257
+ * (set by storeServerTransaction), for the pre-sign confirmation prompt.
6258
+ * Returns null when nothing is buffered (e.g. sign_typed_data, which has
6259
+ * no tx_ready payload — callers fall back to the tool input).
6260
+ */
6261
+ getPendingSummary() {
6262
+ const stored = this.pendingPayloads.get("latest");
6263
+ if (!stored) return null;
6264
+ const p = stored.payload;
6265
+ const labels = p?.resolved?.labels ?? {};
6266
+ const isSwap = !!(p?.approvalTxArgs || p?.swap_tx || labels.quote_summary || labels.to_token_symbol);
6267
+ if (isSwap) {
6268
+ const usedQuoteSummary = !!labels.quote_summary;
6269
+ const head = labels.quote_summary || `swap ${labels.amount_in ?? p?.txArgs?.amount ?? "?"} ${labels.from_token_symbol ?? ""} \u2192 ${labels.to_token_symbol ?? ""}`.trim();
6270
+ const parts = [head, `on ${stored.chain}`];
6271
+ if (!usedQuoteSummary && labels.provider) parts.push(`via ${labels.provider}`);
6272
+ if (p?.__multiLeg) parts.push("(+ token approval \u2014 2 transactions)");
6273
+ if (labels.estimated_fee) parts.push(`est. fee ${labels.estimated_fee}`);
6274
+ return parts.join(" ");
6275
+ }
6276
+ const amount = labels.resolved_amount ?? p?.txArgs?.amount ?? "?";
6277
+ const symbol = labels.token_resolved || labels.token_symbol || "";
6278
+ const amountWithSymbol = symbol && !amount.endsWith(` ${symbol}`) ? `${amount} ${symbol}` : amount;
6279
+ const to = p?.txArgs?.to || labels.recipient_echo || "?";
6280
+ return `send ${amountWithSymbol} on ${stored.chain} to ${to}`;
6281
+ }
6190
6282
  /**
6191
6283
  * Wrap a per-tool handler body with normalised success/failure → RecentAction
6192
6284
  * conversion. Replaces the legacy executeAction → ActionResult adapter that
@@ -8077,6 +8169,34 @@ var AgentSession = class {
8077
8169
  * prompt was declined).
8078
8170
  */
8079
8171
  async runPasswordGatedTool(toolName, toolCallId, ui, body, input) {
8172
+ if (PASSWORD_REQUIRED_TOOLS.has(toolName)) {
8173
+ const summary = (toolName === "sign_tx" ? this.executor.getPendingSummary() : null) ?? `${toolName}${input ? ` ${JSON.stringify(input)}` : ""}`;
8174
+ const approved = await ui.requestConfirmation(summary);
8175
+ if (!approved) {
8176
+ if (toolName === "sign_tx") {
8177
+ this.executor.clearPendingTransaction();
8178
+ }
8179
+ const declined = {
8180
+ tool: toolName,
8181
+ success: false,
8182
+ data: {
8183
+ error: "Transaction not confirmed",
8184
+ code: "CONFIRMATION_REQUIRED" /* CONFIRMATION_REQUIRED */,
8185
+ proposed: summary
8186
+ }
8187
+ };
8188
+ ui.onToolCall(toolCallId, toolName, input);
8189
+ ui.onToolResult(
8190
+ toolCallId,
8191
+ toolName,
8192
+ false,
8193
+ declined.data,
8194
+ "Transaction not confirmed",
8195
+ "CONFIRMATION_REQUIRED" /* CONFIRMATION_REQUIRED */
8196
+ );
8197
+ return declined;
8198
+ }
8199
+ }
8080
8200
  let promptedPassword;
8081
8201
  if (PASSWORD_REQUIRED_TOOLS.has(toolName) && !this.config.password) {
8082
8202
  try {
@@ -8641,20 +8761,35 @@ async function executeAgentAsk(ctx2, message, options) {
8641
8761
  profile: options.profile ?? process.env.VULTISIG_AGENT_PROFILE ?? ""
8642
8762
  };
8643
8763
  const session = new AgentSession(vault, config);
8644
- const ask = new AskInterface(session, !!config.verbose);
8764
+ const ask = new AskInterface(session, !!config.verbose, !!options.autoApprove);
8645
8765
  const callbacks = ask.getCallbacks();
8646
8766
  await session.initialize(callbacks);
8647
8767
  const result = await ask.ask(message);
8768
+ const confirmationRequired = result.toolCalls.some((tc) => tc.code === "CONFIRMATION_REQUIRED" /* CONFIRMATION_REQUIRED */);
8769
+ const proposedCall = result.toolCalls.find(
8770
+ (tc) => tc.code === "CONFIRMATION_REQUIRED" /* CONFIRMATION_REQUIRED */ && typeof tc.data?.proposed === "string"
8771
+ );
8772
+ const proposed = proposedCall?.data?.proposed;
8648
8773
  if (options.json || isJsonOutput()) {
8649
8774
  outputJson({
8650
8775
  session_id: result.sessionId,
8651
8776
  response: result.response,
8652
8777
  tool_calls: result.toolCalls,
8653
- transactions: result.transactions
8778
+ transactions: result.transactions,
8779
+ ...confirmationRequired ? { confirmation_required: true } : {},
8780
+ ...proposed ? { proposed } : {}
8654
8781
  });
8655
8782
  } else {
8656
8783
  process.stdout.write(`session:${result.sessionId}
8657
8784
  `);
8785
+ if (confirmationRequired) {
8786
+ process.stdout.write(`confirmation-required:pass --yes to authorize signing
8787
+ `);
8788
+ if (proposed) {
8789
+ process.stdout.write(`proposed:${proposed}
8790
+ `);
8791
+ }
8792
+ }
8658
8793
  if (result.response) {
8659
8794
  process.stdout.write(`
8660
8795
  ${result.response}
@@ -8768,7 +8903,7 @@ var cachedVersion = null;
8768
8903
  function getVersion() {
8769
8904
  if (cachedVersion) return cachedVersion;
8770
8905
  if (true) {
8771
- cachedVersion = "1.8.1";
8906
+ cachedVersion = "2.0.0";
8772
8907
  return cachedVersion;
8773
8908
  }
8774
8909
  try {
@@ -11488,13 +11623,21 @@ var agentCmd = program.command("agent").description("AI-powered chat interface f
11488
11623
  });
11489
11624
  }
11490
11625
  );
11491
- 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(
11626
+ 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").option(
11627
+ "--yes",
11628
+ "Auto-approve signing/broadcast. Required for unattended signing; default is to NOT broadcast and report the proposed transaction instead."
11629
+ ).addHelpText(
11492
11630
  "after",
11493
11631
  `
11494
11632
  Examples:
11495
11633
  vultisig agent ask "What is my ETH balance?" --output json
11496
- vultisig agent ask "Send 0.1 ETH to 0x..." --session abc123
11497
- vultisig agent ask "..." --profile station-wallet`
11634
+ vultisig agent ask "Send 0.1 ETH to 0x..." --session abc123 --yes
11635
+ vultisig agent ask "..." --profile station-wallet
11636
+
11637
+ Signing safety:
11638
+ Without --yes, ask mode never signs or broadcasts \u2014 it reports the proposed
11639
+ transaction so a read-only prompt can't move funds. Pass --yes to opt in to
11640
+ unattended signing.`
11498
11641
  ).action(
11499
11642
  async (message, options) => {
11500
11643
  const parentOpts = agentCmd.opts();
@@ -11504,7 +11647,8 @@ Examples:
11504
11647
  backendUrl: options.backendUrl || parentOpts.backendUrl,
11505
11648
  password: options.password || parentOpts.password,
11506
11649
  verbose: options.verbose || parentOpts.verbose,
11507
- profile: options.profile ?? parentOpts.profile
11650
+ profile: options.profile ?? parentOpts.profile,
11651
+ autoApprove: options.yes
11508
11652
  });
11509
11653
  }
11510
11654
  );
@@ -11649,8 +11793,3 @@ if (isInteractiveMode) {
11649
11793
  } else {
11650
11794
  program.parse();
11651
11795
  }
11652
- /*! Bundled license information:
11653
-
11654
- @noble/hashes/esm/utils.js:
11655
- (*! noble-hashes - MIT License (c) 2022 Paul Miller (paulmillr.com) *)
11656
- */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vultisig/cli",
3
- "version": "1.8.1",
3
+ "version": "2.0.0",
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": {
@@ -67,35 +67,35 @@
67
67
  },
68
68
  "homepage": "https://vultisig.com",
69
69
  "dependencies": {
70
- "@cosmjs/cosmwasm-stargate": "^0.38.1",
70
+ "@cosmjs/cosmwasm-stargate": "^0.39.0",
71
71
  "@cosmjs/encoding": "^0.39.0",
72
- "@cosmjs/proto-signing": "^0.38.1",
73
- "@cosmjs/stargate": "^0.38.1",
72
+ "@cosmjs/proto-signing": "^0.39.0",
73
+ "@cosmjs/stargate": "^0.39.0",
74
74
  "@napi-rs/keyring": "^1.3.0",
75
- "@noble/hashes": "^2.0.1",
76
- "@vultisig/client-shared": "^0.2.13",
77
- "@vultisig/core-chain": "^2.10.1",
78
- "@vultisig/rujira": "^32.0.0",
79
- "@vultisig/sdk": "^1.8.1",
75
+ "@noble/hashes": "^2.2.0",
76
+ "@vultisig/client-shared": "^0.2.15",
77
+ "@vultisig/core-chain": "^2.15.0",
78
+ "@vultisig/rujira": "^33.0.0",
79
+ "@vultisig/sdk": "^2.0.0",
80
80
  "chalk": "^5.6.2",
81
81
  "cli-table3": "^0.6.5",
82
- "commander": "^14.0.3",
83
- "dotenv": "^17.3.1",
82
+ "commander": "^15.0.0",
83
+ "dotenv": "^17.4.2",
84
84
  "ora": "^9.4.0",
85
85
  "qrcode-terminal": "^0.12.0",
86
86
  "tabtab": "^3.0.2",
87
- "viem": "^2.51.0",
87
+ "viem": "^2.52.2",
88
88
  "ws": "^8.21.0"
89
89
  },
90
90
  "devDependencies": {
91
91
  "@types/inquirer": "^9.0.9",
92
- "@types/node": "^25.5.0",
92
+ "@types/node": "^25.9.2",
93
93
  "@types/tabtab": "^3.0.4",
94
94
  "@types/ws": "^8.18.1",
95
- "esbuild": "^0.27.4",
96
- "tsx": "^4.22.3",
95
+ "esbuild": "^0.28.0",
96
+ "tsx": "^4.22.4",
97
97
  "typescript": "^6.0.3",
98
- "vitest": "^4.1.6"
98
+ "vitest": "^4.1.8"
99
99
  },
100
100
  "engines": {
101
101
  "node": ">=20.0.0"