@vultisig/cli 0.15.0 → 0.15.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,32 @@
1
1
  # @vultisig/cli
2
2
 
3
+ ## 0.15.4
4
+
5
+ ### Patch Changes
6
+
7
+ - [#276](https://github.com/vultisig/vultisig-sdk/pull/276) [`59382c1`](https://github.com/vultisig/vultisig-sdk/commit/59382c1859512fbd362962ede5e92b100d3a5921) Thanks [@rcoderdev](https://github.com/rcoderdev)! - feat(cli): structured machine-readable errors for agent ask, pipe, and executor
8
+ - `agent ask --json` failures include stable `code` with existing `error` string
9
+ - NDJSON pipe `error` events and failed `tool_result` lines include `code`
10
+ - executor `ActionResult` failures carry `AgentErrorCode`; SSE errors accept optional backend `code`
11
+ - document error codes in CLI README
12
+
13
+ - Updated dependencies [[`59382c1`](https://github.com/vultisig/vultisig-sdk/commit/59382c1859512fbd362962ede5e92b100d3a5921)]:
14
+ - @vultisig/sdk@0.15.4
15
+ - @vultisig/rujira@10.0.0
16
+
17
+ ## 0.15.2
18
+
19
+ ### Patch Changes
20
+
21
+ - [#263](https://github.com/vultisig/vultisig-sdk/pull/263) [`6585c38`](https://github.com/vultisig/vultisig-sdk/commit/6585c38431db063f600e133d1a23f84b7c19e934) Thanks [@rcoderdev](https://github.com/rcoderdev)! - fix(cli): align agent executor with backend payloads and harden action handling
22
+ - model `tx_ready` / non-streaming transaction payloads with `TxReadyPayload`
23
+ - optional `vultisig` on agent config for shared SDK state (e.g. address book)
24
+ - executor improvements (chain locks, calldata resolution, EVM gas refresh) and unit tests
25
+
26
+ - Updated dependencies [[`6585c38`](https://github.com/vultisig/vultisig-sdk/commit/6585c38431db063f600e133d1a23f84b7c19e934)]:
27
+ - @vultisig/sdk@0.15.2
28
+ - @vultisig/rujira@10.0.0
29
+
3
30
  ## 0.15.0
4
31
 
5
32
  ### Patch Changes
package/README.md CHANGED
@@ -658,7 +658,7 @@ explorer:https://etherscan.io/tx/0x9f8e7d6c...
658
658
  "session_id": "abc123-def456",
659
659
  "response": "Your ETH balance is 1.5 ETH ($3,750.00 USD).",
660
660
  "tool_calls": [
661
- { "action": "getBalance", "success": true, "data": { "chain": "ethereum", "balance": "1.5" } }
661
+ { "action": "get_balances", "success": true, "data": { "balances": [{ "chain": "Ethereum", "symbol": "ETH", "amount": "1.5", "decimals": 18, "raw_amount": "1500000000000000000" }] } }
662
662
  ],
663
663
  "transactions": [
664
664
  { "hash": "0x9f8e7d6c...", "chain": "ethereum", "explorerUrl": "https://etherscan.io/tx/0x9f8e7d6c..." }
@@ -666,6 +666,36 @@ explorer:https://etherscan.io/tx/0x9f8e7d6c...
666
666
  }
667
667
  ```
668
668
 
669
+ On failure, stdout is a single JSON object with both a human `error` string and a stable `code` (the `error` field is unchanged for older parsers):
670
+
671
+ ```json
672
+ { "error": "Agent backend unreachable at https://example.invalid", "code": "BACKEND_UNREACHABLE" }
673
+ ```
674
+
675
+ Each entry in `tool_calls` may include `code` when `success` is false (same values as below).
676
+
677
+ ##### Error codes (`agent ask --json`, `--via-agent`, executor)
678
+
679
+ Orchestrators should branch on `code`. The message in `error` / `message` stays human-readable and may change between releases.
680
+
681
+ | Code | Typical meaning |
682
+ |------|-----------------|
683
+ | `BACKEND_UNREACHABLE` | Agent health check failed or backend not responding |
684
+ | `AUTH_FAILED` | Auth/token failure, HTTP 401/403, or wrong vault password |
685
+ | `VAULT_LOCKED` | Encrypted vault needs unlock (password) |
686
+ | `PASSWORD_REQUIRED` | Password was not supplied when required (e.g. pipe mode or signing) |
687
+ | `CONFIRMATION_REQUIRED` | User confirmation needed (pipe mode; message prefix `CONFIRMATION_REQUIRED:`) |
688
+ | `ACTION_NOT_IMPLEMENTED` | Local executor does not implement this action type |
689
+ | `INVALID_INPUT` | Bad parameters, unknown chain, malformed NDJSON input, etc. |
690
+ | `NETWORK_ERROR` | RPC/fetch connectivity (includes many SDK `VaultError` network cases) |
691
+ | `TIMEOUT` | Deadline exceeded, or abort where the message indicates a timeout |
692
+ | `TRANSACTION_FAILED` | Build/broadcast/gas errors mapped from the SDK |
693
+ | `SIGNING_FAILED` | MPC/signing failed |
694
+ | `SESSION_NOT_INITIALIZED` | Internal session state error |
695
+ | `UNKNOWN_ERROR` | Unclassified failure (default for opaque SSE `error` events). Plain `AbortError` without “timeout” in the message maps here. |
696
+
697
+ SSE `error` events may optionally include a `code` field from the backend; if it matches one of the values above, it is passed through unchanged. Otherwise the CLI infers a code from the message.
698
+
669
699
  **Agent ask options:**
670
700
  - `--session <id>` - Continue an existing conversation
671
701
  - `--backend-url <url>` - Agent backend URL (default: https://abe.vultisig.com)
@@ -712,15 +742,13 @@ The pipe interface uses NDJSON (one JSON object per line) on stdin/stdout. Desig
712
742
  | `ready` | `vault, addresses` | Session initialized, addresses for all chains |
713
743
  | `session` | `id` | Conversation ID for resuming later |
714
744
  | `history` | `messages[]` | Previous messages when resuming a session |
715
- | `auth` | `status, error?` | Authentication result (`authenticated` or `failed`) |
716
- | `conversation` | `id` | Conversation created or resumed |
717
745
  | `text_delta` | `delta` | Streaming text chunk from the agent |
718
746
  | `tool_call` | `id, action, params?, status` | Action started (`running`) |
719
- | `tool_result` | `id, action, success, data?, error?` | Action completed |
747
+ | `tool_result` | `id, action, success, data?, error?, code?` | Action completed (`code` when `success` is false) |
720
748
  | `tx_status` | `tx_hash, chain, status, explorer_url?` | Transaction broadcast/confirmed/failed |
721
749
  | `assistant` | `content` | Full assistant response |
722
750
  | `suggestions` | `suggestions[]` | Suggested follow-up actions |
723
- | `error` | `message` | Error (includes `PASSWORD_REQUIRED` and `CONFIRMATION_REQUIRED` signals) |
751
+ | `error` | `message, code` | Error or control signal (`PASSWORD_REQUIRED`, `CONFIRMATION_REQUIRED: …`; always includes stable `code`) |
724
752
  | `done` | `{}` | Response cycle complete |
725
753
 
726
754
  **Example session:**
@@ -740,7 +768,7 @@ echo '{"type":"message","content":"What is my ETH balance?"}' | vultisig agent -
740
768
  {"type":"done"}
741
769
  ```
742
770
 
743
- When the agent needs a password mid-session (e.g. for signing), it emits `{"type":"error","message":"PASSWORD_REQUIRED"}`. Respond with `{"type":"password","password":"..."}` on stdin.
771
+ When the agent needs a password mid-session (e.g. for signing), it emits `{"type":"error","message":"PASSWORD_REQUIRED","code":"PASSWORD_REQUIRED"}`. Respond with `{"type":"password","password":"..."}` on stdin.
744
772
 
745
773
  #### Session Management
746
774
 
package/dist/index.js CHANGED
@@ -1452,7 +1452,7 @@ var init_formatUnits = __esm({
1452
1452
  // src/index.ts
1453
1453
  import "dotenv/config";
1454
1454
  import { promises as fs4 } from "node:fs";
1455
- import { parseKeygenQR, Vultisig as Vultisig7 } from "@vultisig/sdk";
1455
+ import { parseKeygenQR, Vultisig as Vultisig6 } from "@vultisig/sdk";
1456
1456
  import chalk15 from "chalk";
1457
1457
  import { program } from "commander";
1458
1458
  import inquirer8 from "inquirer";
@@ -3934,7 +3934,12 @@ Address Book${options.chain ? ` (${options.chain})` : ""}:
3934
3934
  }
3935
3935
 
3936
3936
  // src/commands/rujira.ts
3937
- import { getRoutesSummary, listEasyRoutes, RujiraClient, VultisigRujiraProvider } from "@vultisig/rujira";
3937
+ import {
3938
+ getRoutesSummary,
3939
+ listEasyRoutes,
3940
+ RujiraClient,
3941
+ VultisigRujiraProvider
3942
+ } from "@vultisig/rujira";
3938
3943
  async function createRujiraClient(ctx2, options = {}) {
3939
3944
  const vault = await ctx2.ensureActiveVault();
3940
3945
  const provider = new VultisigRujiraProvider(vault);
@@ -4221,6 +4226,122 @@ function displayDiscountTier(tierInfo) {
4221
4226
  import chalk9 from "chalk";
4222
4227
  import Table from "cli-table3";
4223
4228
 
4229
+ // src/agent/agentErrors.ts
4230
+ import { VaultError, VaultErrorCode, VaultImportError, VaultImportErrorCode } from "@vultisig/sdk";
4231
+ var AgentErrorCode = /* @__PURE__ */ ((AgentErrorCode3) => {
4232
+ AgentErrorCode3["BACKEND_UNREACHABLE"] = "BACKEND_UNREACHABLE";
4233
+ AgentErrorCode3["AUTH_FAILED"] = "AUTH_FAILED";
4234
+ AgentErrorCode3["VAULT_LOCKED"] = "VAULT_LOCKED";
4235
+ AgentErrorCode3["PASSWORD_REQUIRED"] = "PASSWORD_REQUIRED";
4236
+ AgentErrorCode3["CONFIRMATION_REQUIRED"] = "CONFIRMATION_REQUIRED";
4237
+ AgentErrorCode3["ACTION_NOT_IMPLEMENTED"] = "ACTION_NOT_IMPLEMENTED";
4238
+ AgentErrorCode3["INVALID_INPUT"] = "INVALID_INPUT";
4239
+ AgentErrorCode3["NETWORK_ERROR"] = "NETWORK_ERROR";
4240
+ AgentErrorCode3["TIMEOUT"] = "TIMEOUT";
4241
+ AgentErrorCode3["TRANSACTION_FAILED"] = "TRANSACTION_FAILED";
4242
+ AgentErrorCode3["SIGNING_FAILED"] = "SIGNING_FAILED";
4243
+ AgentErrorCode3["SESSION_NOT_INITIALIZED"] = "SESSION_NOT_INITIALIZED";
4244
+ AgentErrorCode3["UNKNOWN_ERROR"] = "UNKNOWN_ERROR";
4245
+ return AgentErrorCode3;
4246
+ })(AgentErrorCode || {});
4247
+ var AGENT_ERROR_CODE_VALUES = new Set(Object.values(AgentErrorCode));
4248
+ function isAgentErrorCode(value) {
4249
+ return AGENT_ERROR_CODE_VALUES.has(value);
4250
+ }
4251
+ function mapVaultError(err) {
4252
+ if (err.code === VaultErrorCode.InvalidConfig && /failed to unlock vault/i.test(err.message)) {
4253
+ return "AUTH_FAILED" /* AUTH_FAILED */;
4254
+ }
4255
+ switch (err.code) {
4256
+ case VaultErrorCode.Timeout:
4257
+ return "TIMEOUT" /* TIMEOUT */;
4258
+ case VaultErrorCode.NetworkError:
4259
+ case VaultErrorCode.BalanceFetchFailed:
4260
+ return "NETWORK_ERROR" /* NETWORK_ERROR */;
4261
+ case VaultErrorCode.SigningFailed:
4262
+ return "SIGNING_FAILED" /* SIGNING_FAILED */;
4263
+ case VaultErrorCode.BroadcastFailed:
4264
+ case VaultErrorCode.GasEstimationFailed:
4265
+ return "TRANSACTION_FAILED" /* TRANSACTION_FAILED */;
4266
+ case VaultErrorCode.NotImplemented:
4267
+ return "ACTION_NOT_IMPLEMENTED" /* ACTION_NOT_IMPLEMENTED */;
4268
+ case VaultErrorCode.InvalidAmount:
4269
+ case VaultErrorCode.UnsupportedChain:
4270
+ case VaultErrorCode.UnsupportedToken:
4271
+ case VaultErrorCode.ChainNotSupported:
4272
+ case VaultErrorCode.InvalidVault:
4273
+ case VaultErrorCode.InvalidPublicKey:
4274
+ case VaultErrorCode.InvalidChainCode:
4275
+ case VaultErrorCode.AddressDerivationFailed:
4276
+ return "INVALID_INPUT" /* INVALID_INPUT */;
4277
+ case VaultErrorCode.InvalidConfig:
4278
+ return "INVALID_INPUT" /* INVALID_INPUT */;
4279
+ default:
4280
+ return "UNKNOWN_ERROR" /* UNKNOWN_ERROR */;
4281
+ }
4282
+ }
4283
+ function mapVaultImportError(err) {
4284
+ switch (err.code) {
4285
+ case VaultImportErrorCode.PASSWORD_REQUIRED:
4286
+ return "PASSWORD_REQUIRED" /* PASSWORD_REQUIRED */;
4287
+ case VaultImportErrorCode.INVALID_PASSWORD:
4288
+ return "AUTH_FAILED" /* AUTH_FAILED */;
4289
+ default:
4290
+ return "INVALID_INPUT" /* INVALID_INPUT */;
4291
+ }
4292
+ }
4293
+ function networkishMessage(msg) {
4294
+ return /ECONNREFUSED|ENOTFOUND|ETIMEDOUT|network|fetch failed|socket/i.test(msg) || /getaddrinfo|certificate|TLS|SSL/i.test(msg);
4295
+ }
4296
+ function inferAgentErrorCodeFromMessage(message) {
4297
+ const m = message.trim();
4298
+ if (!m) return "UNKNOWN_ERROR" /* UNKNOWN_ERROR */;
4299
+ if (/agent backend unreachable/i.test(m)) return "BACKEND_UNREACHABLE" /* BACKEND_UNREACHABLE */;
4300
+ if (/authentication failed|^auth failed/i.test(m)) return "AUTH_FAILED" /* AUTH_FAILED */;
4301
+ if (m === "PASSWORD_REQUIRED") return "PASSWORD_REQUIRED" /* PASSWORD_REQUIRED */;
4302
+ if (/^CONFIRMATION_REQUIRED:/i.test(m)) return "CONFIRMATION_REQUIRED" /* CONFIRMATION_REQUIRED */;
4303
+ if (/password required|password not provided|use --password/i.test(m)) return "PASSWORD_REQUIRED" /* PASSWORD_REQUIRED */;
4304
+ if (/session not initialized/i.test(m)) return "SESSION_NOT_INITIALIZED" /* SESSION_NOT_INITIALIZED */;
4305
+ if (/not implemented locally|is not yet implemented|is not implemented locally|action type .*not implemented/i.test(m)) {
4306
+ return "ACTION_NOT_IMPLEMENTED" /* ACTION_NOT_IMPLEMENTED */;
4307
+ }
4308
+ if (/\(401\)|\(403\)|\b401\b|\b403\b|unauthorized|forbidden/i.test(m)) return "AUTH_FAILED" /* AUTH_FAILED */;
4309
+ if (/failed to unlock vault/i.test(m)) return "AUTH_FAILED" /* AUTH_FAILED */;
4310
+ if (/vault.*locked|must unlock|unlock.*vault/i.test(m)) return "VAULT_LOCKED" /* VAULT_LOCKED */;
4311
+ if (/timed out|timeout/i.test(m)) return "TIMEOUT" /* TIMEOUT */;
4312
+ if (networkishMessage(m)) return "NETWORK_ERROR" /* NETWORK_ERROR */;
4313
+ if (/unknown chain|unknown from_chain|unknown to_chain/i.test(m) || /\bis required\b|\bmissing\b|\brequires\b/i.test(m) || /no pending transaction|invalid or empty tx|could not stage calldata|server transaction missing/i.test(m) || /build_custom_tx requires|incomplete for a contract call|invalid der:/i.test(m)) {
4314
+ return "INVALID_INPUT" /* INVALID_INPUT */;
4315
+ }
4316
+ return "UNKNOWN_ERROR" /* UNKNOWN_ERROR */;
4317
+ }
4318
+ function nodeErrCode(err) {
4319
+ if (err && typeof err === "object" && "code" in err && typeof err.code === "string") {
4320
+ return err.code;
4321
+ }
4322
+ return void 0;
4323
+ }
4324
+ function normalizeAgentError(err) {
4325
+ if (err instanceof VaultError) {
4326
+ return { code: mapVaultError(err), message: err.message };
4327
+ }
4328
+ if (err instanceof VaultImportError) {
4329
+ return { code: mapVaultImportError(err), message: err.message };
4330
+ }
4331
+ const name = err instanceof Error ? err.name : "";
4332
+ if (name === "AbortError") {
4333
+ const message2 = err instanceof Error ? err.message : "Aborted";
4334
+ const code = /timed out|timeout/i.test(message2) ? "TIMEOUT" /* TIMEOUT */ : "UNKNOWN_ERROR" /* UNKNOWN_ERROR */;
4335
+ return { code, message: message2 };
4336
+ }
4337
+ const message = err instanceof Error ? err.message : String(err);
4338
+ const nc = nodeErrCode(err);
4339
+ if (nc === "ECONNREFUSED" || nc === "ENOTFOUND" || nc === "ETIMEDOUT") {
4340
+ return { code: "NETWORK_ERROR" /* NETWORK_ERROR */, message };
4341
+ }
4342
+ return { code: inferAgentErrorCodeFromMessage(message), message };
4343
+ }
4344
+
4224
4345
  // src/agent/ask.ts
4225
4346
  var AskInterface = class {
4226
4347
  session;
@@ -4247,10 +4368,10 @@ var AskInterface = class {
4247
4368
  `);
4248
4369
  }
4249
4370
  },
4250
- onToolResult: (_id, action, success2, data, error2) => {
4251
- this.toolCalls.push({ action, success: success2, data, error: error2 });
4371
+ onToolResult: (_id, action, success2, data, error2, code) => {
4372
+ this.toolCalls.push({ action, success: success2, data, error: error2, code });
4252
4373
  if (this.verbose) {
4253
- const status = success2 ? "ok" : `error: ${error2}`;
4374
+ const status = success2 ? "ok" : `error: ${error2}${code ? ` [${code}]` : ""}`;
4254
4375
  process.stderr.write(`[tool] ${action}: ${status}
4255
4376
  `);
4256
4377
  }
@@ -4269,8 +4390,8 @@ var AskInterface = class {
4269
4390
  `);
4270
4391
  }
4271
4392
  },
4272
- onError: (message) => {
4273
- process.stderr.write(`[error] ${message}
4393
+ onError: (message, code) => {
4394
+ process.stderr.write(`[error] ${message} [${code}]
4274
4395
  `);
4275
4396
  },
4276
4397
  onDone: () => {
@@ -4401,6 +4522,17 @@ function padTo32Bytes(buf) {
4401
4522
  }
4402
4523
 
4403
4524
  // src/agent/client.ts
4525
+ function sseErrorToMessage(value) {
4526
+ if (value == null) return "";
4527
+ if (typeof value === "string") return value;
4528
+ if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") return String(value);
4529
+ if (value instanceof Error) return value.message;
4530
+ try {
4531
+ return JSON.stringify(value);
4532
+ } catch {
4533
+ return String(value);
4534
+ }
4535
+ }
4404
4536
  var AgentClient = class {
4405
4537
  baseUrl;
4406
4538
  authToken = null;
@@ -4455,7 +4587,9 @@ var AgentClient = class {
4455
4587
  return this.post(`/agent/conversations/${conversationId}`, req);
4456
4588
  }
4457
4589
  async deleteConversation(conversationId, publicKey) {
4458
- await this.delete(`/agent/conversations/${conversationId}`, { public_key: publicKey });
4590
+ await this.delete(`/agent/conversations/${conversationId}`, {
4591
+ public_key: publicKey
4592
+ });
4459
4593
  }
4460
4594
  // ============================================================================
4461
4595
  // Messages - JSON mode
@@ -4566,16 +4700,22 @@ var AgentClient = class {
4566
4700
  case "tx_ready":
4567
4701
  if (this.verbose) process.stderr.write(`[SSE:tx_ready] raw: ${data.slice(0, 2e3)}
4568
4702
  `);
4569
- result.transactions.push(parsed);
4570
- callbacks.onTxReady?.(parsed);
4703
+ {
4704
+ const txReady = parsed;
4705
+ result.transactions.push(txReady);
4706
+ callbacks.onTxReady?.(txReady);
4707
+ }
4571
4708
  break;
4572
4709
  case "message":
4573
4710
  result.message = parsed.message || parsed;
4574
4711
  callbacks.onMessage?.(result.message);
4575
4712
  break;
4576
- case "error":
4577
- callbacks.onError?.(parsed.error);
4713
+ case "error": {
4714
+ const msg = sseErrorToMessage(parsed.error);
4715
+ const codeFromBackend = typeof parsed.code === "string" && isAgentErrorCode(parsed.code) ? parsed.code : inferAgentErrorCodeFromMessage(msg);
4716
+ callbacks.onError?.(msg, codeFromBackend);
4578
4717
  break;
4718
+ }
4579
4719
  case "done":
4580
4720
  break;
4581
4721
  }
@@ -4782,7 +4922,7 @@ function getNativeTokenDecimals(chain) {
4782
4922
  }
4783
4923
 
4784
4924
  // src/agent/executor.ts
4785
- import { Chain as Chain9, Vultisig as Vultisig6 } from "@vultisig/sdk";
4925
+ import { Chain as Chain9, evmCall, fiatCurrencies as fiatCurrencies3, Vultisig as VultisigSdk } from "@vultisig/sdk";
4786
4926
 
4787
4927
  // node_modules/viem/_esm/index.js
4788
4928
  init_formatUnits();
@@ -4999,6 +5139,8 @@ var EVM_GAS_RPC = {
4999
5139
  };
5000
5140
  var AgentExecutor = class {
5001
5141
  vault;
5142
+ /** Owning SDK (optional); used for address book backed by app storage */
5143
+ vultisig;
5002
5144
  pendingPayloads = /* @__PURE__ */ new Map();
5003
5145
  password = null;
5004
5146
  verbose;
@@ -5007,9 +5149,10 @@ var AgentExecutor = class {
5007
5149
  chainLockReleases = /* @__PURE__ */ new Map();
5008
5150
  /** Backend client for resolving calldata_id references. */
5009
5151
  backendClient = null;
5010
- constructor(vault, verbose = false, vaultId) {
5152
+ constructor(vault, verbose = false, vaultId, vultisig) {
5011
5153
  this.vault = vault;
5012
5154
  this.verbose = verbose;
5155
+ this.vultisig = vultisig;
5013
5156
  if (vaultId) {
5014
5157
  this.stateStore = new VaultStateStore(vaultId);
5015
5158
  }
@@ -5023,6 +5166,8 @@ var AgentExecutor = class {
5023
5166
  /**
5024
5167
  * Store a server-built transaction (from tx_ready SSE event).
5025
5168
  * This allows sign_tx to find and sign it when the backend requests signing.
5169
+ *
5170
+ * @returns true when a signable payload was stored; false for MCP errors or missing tx body
5026
5171
  */
5027
5172
  storeServerTransaction(txReadyData) {
5028
5173
  if (this.verbose)
@@ -5030,11 +5175,17 @@ var AgentExecutor = class {
5030
5175
  `[executor] storeServerTransaction called, keys: ${Object.keys(txReadyData || {}).join(",")}
5031
5176
  `
5032
5177
  );
5033
- const swapTx = txReadyData.swap_tx || txReadyData.send_tx || txReadyData.tx;
5034
- if (!swapTx) {
5178
+ const nestedTx = txReadyData?.swap_tx || txReadyData?.send_tx || txReadyData?.tx;
5179
+ if (nestedTx?.status === "error" || nestedTx?.error) {
5180
+ if (this.verbose)
5181
+ process.stderr.write(`[executor] skipping error tx_ready: ${nestedTx.error || "unknown error"}
5182
+ `);
5183
+ return false;
5184
+ }
5185
+ if (!nestedTx) {
5035
5186
  if (this.verbose) process.stderr.write(`[executor] storeServerTransaction: no swap_tx/send_tx/tx found in data
5036
5187
  `);
5037
- return;
5188
+ return false;
5038
5189
  }
5039
5190
  const chain = resolveChainFromTxReady(txReadyData) || Chain9.Ethereum;
5040
5191
  this.pendingPayloads.clear();
@@ -5049,6 +5200,7 @@ var AgentExecutor = class {
5049
5200
  `[executor] Stored server tx for chain ${chain}, pendingPayloads size=${this.pendingPayloads.size}
5050
5201
  `
5051
5202
  );
5203
+ return true;
5052
5204
  }
5053
5205
  hasPendingTransaction() {
5054
5206
  return this.pendingPayloads.has("latest");
@@ -5072,11 +5224,13 @@ var AgentExecutor = class {
5072
5224
  data
5073
5225
  };
5074
5226
  } catch (err) {
5227
+ const { code, message } = normalizeAgentError(err);
5075
5228
  return {
5076
5229
  action: action.type,
5077
5230
  action_id: action.id,
5078
5231
  success: false,
5079
- error: err.message || String(err)
5232
+ error: message,
5233
+ code
5080
5234
  };
5081
5235
  }
5082
5236
  }
@@ -5107,7 +5261,7 @@ var AgentExecutor = class {
5107
5261
  case "sign_tx":
5108
5262
  return this.signTx(params);
5109
5263
  case "get_address_book":
5110
- return this.getAddressBook();
5264
+ return this.getAddressBook(params);
5111
5265
  case "address_book_add":
5112
5266
  return this.addAddressBookEntry(params);
5113
5267
  case "address_book_remove":
@@ -5153,15 +5307,34 @@ var AgentExecutor = class {
5153
5307
  }
5154
5308
  return { balances: entries };
5155
5309
  }
5156
- async getPortfolio(_params) {
5157
- const balanceRecord = await this.vault.balances();
5158
- const entries = Object.entries(balanceRecord).map(([key, b]) => ({
5159
- chain: b.chainId || key.split(":")[0] || "",
5310
+ async getPortfolio(params) {
5311
+ const currencyRaw = String(params.currency ?? "USD").trim().toLowerCase();
5312
+ const fiatCurrency = fiatCurrencies3.includes(currencyRaw) ? currencyRaw : "usd";
5313
+ const portfolio = await this.vault.portfolio(fiatCurrency);
5314
+ const chainFilter = params.chain;
5315
+ const tickerFilter = params.ticker;
5316
+ let rows = portfolio.balances.map((b) => ({
5317
+ chain: b.chainId || "",
5160
5318
  symbol: b.symbol || "",
5161
5319
  amount: b.formattedAmount || b.amount?.toString() || "0",
5162
- decimals: b.decimals || 18
5320
+ decimals: b.decimals ?? 18,
5321
+ raw_amount: b.amount,
5322
+ fiatValue: b.fiatValue,
5323
+ fiatCurrency: b.fiatCurrency ?? portfolio.currency
5163
5324
  }));
5164
- return { balances: entries };
5325
+ if (chainFilter) {
5326
+ const chain = resolveChain(chainFilter);
5327
+ if (!chain) throw new Error(`Unknown chain: ${chainFilter}`);
5328
+ rows = rows.filter((r) => r.chain.toLowerCase() === chain.toLowerCase());
5329
+ }
5330
+ if (tickerFilter) {
5331
+ rows = rows.filter((r) => r.symbol.toLowerCase() === String(tickerFilter).toLowerCase());
5332
+ }
5333
+ return {
5334
+ balances: rows,
5335
+ totalValue: portfolio.totalValue,
5336
+ currency: portfolio.currency
5337
+ };
5165
5338
  }
5166
5339
  // ============================================================================
5167
5340
  // Chain & Token Management
@@ -5261,13 +5434,28 @@ var AgentExecutor = class {
5261
5434
  };
5262
5435
  const amount = parseAmount(amountStr, balance.decimals);
5263
5436
  const memo = params.memo;
5264
- const payload = await this.vault.prepareSendTx({ coin, receiver: toAddress, amount, memo });
5437
+ const payload = await this.vault.prepareSendTx({
5438
+ coin,
5439
+ receiver: toAddress,
5440
+ amount,
5441
+ memo
5442
+ });
5265
5443
  await this.patchEvmNonce(chain, payload);
5266
5444
  const messageHashes = await this.vault.extractMessageHashes(payload);
5267
5445
  this.pendingPayloads.clear();
5268
5446
  const payloadId = `tx_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
5269
- this.pendingPayloads.set(payloadId, { payload, coin, chain, timestamp: Date.now() });
5270
- this.pendingPayloads.set("latest", { payload, coin, chain, timestamp: Date.now() });
5447
+ this.pendingPayloads.set(payloadId, {
5448
+ payload,
5449
+ coin,
5450
+ chain,
5451
+ timestamp: Date.now()
5452
+ });
5453
+ this.pendingPayloads.set("latest", {
5454
+ payload,
5455
+ coin,
5456
+ chain,
5457
+ timestamp: Date.now()
5458
+ });
5271
5459
  return {
5272
5460
  keysign_payload: payloadId,
5273
5461
  from_chain: chain.toString(),
@@ -5307,7 +5495,10 @@ var AgentExecutor = class {
5307
5495
  const fromToken = params.from_contract || params.from_token_id;
5308
5496
  const toToken = params.to_contract || params.to_token_id;
5309
5497
  const fromCoin = { chain: fromChain, token: fromToken || void 0 };
5310
- const toCoin = { chain: toChain || fromChain, token: toToken || void 0 };
5498
+ const toCoin = {
5499
+ chain: toChain || fromChain,
5500
+ token: toToken || void 0
5501
+ };
5311
5502
  const quote = await this.vault.getSwapQuote({
5312
5503
  fromCoin,
5313
5504
  toCoin,
@@ -5377,11 +5568,14 @@ var AgentExecutor = class {
5377
5568
  chain: params.chain,
5378
5569
  chain_id: params.chain_id
5379
5570
  };
5380
- this.storeServerTransaction({
5571
+ const stored = this.storeServerTransaction({
5381
5572
  tx: txData,
5382
5573
  chain: params.chain,
5383
5574
  from_chain: params.chain
5384
5575
  });
5576
+ if (!stored) {
5577
+ throw new Error("Could not stage calldata transaction for signing (invalid or empty tx payload)");
5578
+ }
5385
5579
  const chain = resolveChain(params.chain) || Chain9.Ethereum;
5386
5580
  const address = await this.vault.address(chain);
5387
5581
  return {
@@ -5454,9 +5648,10 @@ var AgentExecutor = class {
5454
5648
  }
5455
5649
  const { payload, chain } = stored;
5456
5650
  if (payload.__serverTx) {
5651
+ let result;
5457
5652
  if (chain === "Solana" && (payload.swap_tx || payload.provider)) {
5458
5653
  try {
5459
- return await this.buildAndSignSolanaSwapLocally(payload);
5654
+ result = await this.buildAndSignSolanaSwapLocally(payload);
5460
5655
  } catch (e) {
5461
5656
  if (e._phase === "prepare") {
5462
5657
  if (this.verbose)
@@ -5467,7 +5662,9 @@ var AgentExecutor = class {
5467
5662
  }
5468
5663
  }
5469
5664
  }
5470
- return this.signServerTx(payload, chain, params);
5665
+ if (!result) result = await this.signServerTx(payload, chain, params);
5666
+ if (payload.sequence_id) result.sequence_id = payload.sequence_id;
5667
+ return result;
5471
5668
  }
5472
5669
  return this.signSdkTx(payload, chain, payloadId);
5473
5670
  }
@@ -5503,7 +5700,7 @@ var AgentExecutor = class {
5503
5700
  }
5504
5701
  await this.releaseEvmLock(chain);
5505
5702
  this.pendingPayloads.clear();
5506
- const explorerUrl = Vultisig6.getTxExplorerUrl(chain, txHash);
5703
+ const explorerUrl = VultisigSdk.getTxExplorerUrl(chain, txHash);
5507
5704
  return {
5508
5705
  tx_hash: txHash,
5509
5706
  chain: chain.toString(),
@@ -5600,7 +5797,7 @@ var AgentExecutor = class {
5600
5797
  }
5601
5798
  await this.releaseEvmLock(chain);
5602
5799
  this.pendingPayloads.clear();
5603
- const explorerUrl = Vultisig6.getTxExplorerUrl(chain, txHash);
5800
+ const explorerUrl = VultisigSdk.getTxExplorerUrl(chain, txHash);
5604
5801
  return {
5605
5802
  tx_hash: txHash,
5606
5803
  chain: chain.toString(),
@@ -5625,9 +5822,15 @@ var AgentExecutor = class {
5625
5822
  const fromChainName = serverTxData.from_chain || serverTxData.chain || "Solana";
5626
5823
  const toChainName = serverTxData.to_chain;
5627
5824
  const fromChain = resolveChain(fromChainName);
5628
- if (!fromChain) throw Object.assign(new Error(`Unknown from_chain: ${fromChainName}`), { _phase: "prepare" });
5825
+ if (!fromChain)
5826
+ throw Object.assign(new Error(`Unknown from_chain: ${fromChainName}`), {
5827
+ _phase: "prepare"
5828
+ });
5629
5829
  const toChain = toChainName ? resolveChain(toChainName) : fromChain;
5630
- if (!toChain) throw Object.assign(new Error(`Unknown to_chain: ${toChainName}`), { _phase: "prepare" });
5830
+ if (!toChain)
5831
+ throw Object.assign(new Error(`Unknown to_chain: ${toChainName}`), {
5832
+ _phase: "prepare"
5833
+ });
5631
5834
  const amountStr = serverTxData.amount;
5632
5835
  if (!amountStr)
5633
5836
  throw Object.assign(new Error("Missing amount in tx_ready data for local Solana swap build"), {
@@ -5694,7 +5897,7 @@ var AgentExecutor = class {
5694
5897
  signature
5695
5898
  });
5696
5899
  this.pendingPayloads.clear();
5697
- const explorerUrl = Vultisig6.getTxExplorerUrl(chain, txHash);
5900
+ const explorerUrl = VultisigSdk.getTxExplorerUrl(chain, txHash);
5698
5901
  return {
5699
5902
  tx_hash: txHash,
5700
5903
  chain: chain.toString(),
@@ -5942,8 +6145,18 @@ var AgentExecutor = class {
5942
6145
  // ============================================================================
5943
6146
  // Address Book
5944
6147
  // ============================================================================
5945
- async getAddressBook() {
5946
- throw new Error("get_address_book is not yet implemented locally. The backend may handle this action server-side.");
6148
+ async getAddressBook(params) {
6149
+ if (!this.vultisig) {
6150
+ throw new Error(
6151
+ "get_address_book requires the CLI SDK instance. Ensure AgentConfig.vultisig is set when creating the session."
6152
+ );
6153
+ }
6154
+ const chainName = params.chain || params.chain_name;
6155
+ const chain = chainName ? resolveChain(chainName) : void 0;
6156
+ if (chainName && !chain) {
6157
+ throw new Error(`Unknown chain: ${chainName}`);
6158
+ }
6159
+ return await this.vultisig.getAddressBook(chain);
5947
6160
  }
5948
6161
  async addAddressBookEntry(_params) {
5949
6162
  throw new Error("address_book_add is not yet implemented locally. The backend may handle this action server-side.");
@@ -5956,8 +6169,34 @@ var AgentExecutor = class {
5956
6169
  // ============================================================================
5957
6170
  // Token Search & Other
5958
6171
  // ============================================================================
5959
- async searchToken(_params) {
5960
- throw new Error("search_token is not yet implemented locally. The backend may handle this action server-side.");
6172
+ async searchToken(params) {
6173
+ const query = String(params.query ?? params.q ?? "").trim().toLowerCase();
6174
+ if (!query) {
6175
+ return { tokens: [] };
6176
+ }
6177
+ const limit = 20;
6178
+ const chainName = params.chain;
6179
+ const tokenMatchesQuery = (t) => {
6180
+ const tick = t.ticker.toLowerCase();
6181
+ const addr = (t.contractAddress ?? "").toLowerCase();
6182
+ const pid = (t.priceProviderId ?? "").toLowerCase();
6183
+ return tick.includes(query) || addr.includes(query) || pid.includes(query);
6184
+ };
6185
+ if (chainName) {
6186
+ const chain = resolveChain(chainName);
6187
+ if (!chain) throw new Error(`Unknown chain: ${chainName}`);
6188
+ const tokens = VultisigSdk.getKnownTokens(chain).filter(tokenMatchesQuery).slice(0, limit);
6189
+ return { tokens };
6190
+ }
6191
+ const out = [];
6192
+ for (const c of Object.values(Chain9)) {
6193
+ for (const t of VultisigSdk.getKnownTokens(c)) {
6194
+ if (!tokenMatchesQuery(t)) continue;
6195
+ out.push(t);
6196
+ if (out.length >= limit) return { tokens: out };
6197
+ }
6198
+ }
6199
+ return { tokens: out };
5961
6200
  }
5962
6201
  async listVaults() {
5963
6202
  return {
@@ -5974,8 +6213,25 @@ var AgentExecutor = class {
5974
6213
  async scanTx(_params) {
5975
6214
  throw new Error("scan_tx is not yet implemented locally. The backend may handle this action server-side.");
5976
6215
  }
5977
- async readEvmContract(_params) {
5978
- throw new Error("read_evm_contract is not yet implemented locally. The backend may handle this action server-side.");
6216
+ async readEvmContract(params) {
6217
+ const chainName = params.chain;
6218
+ if (!chainName) throw new Error("read_evm_contract requires chain");
6219
+ const contractRaw = params.contract_address || params.contractAddress;
6220
+ if (!contractRaw) throw new Error("read_evm_contract requires contract_address");
6221
+ const functionName = params.function_name || params.functionName;
6222
+ if (!functionName) throw new Error("read_evm_contract requires function_name");
6223
+ const chain = resolveChain(chainName);
6224
+ if (!chain) throw new Error(`Unknown chain: ${chainName}`);
6225
+ if (!EVM_CHAINS.has(chain)) {
6226
+ throw new Error(`read_evm_contract only supports EVM chains (got ${chain})`);
6227
+ }
6228
+ const callParams = params.params ?? [];
6229
+ const data = await encodeContractCall(functionName, callParams);
6230
+ const addr = contractRaw.startsWith("0x") ? contractRaw : `0x${contractRaw}`;
6231
+ const to = addr;
6232
+ const from = params.from;
6233
+ const result = await evmCall(chain, { to, data, from });
6234
+ return { result };
5979
6235
  }
5980
6236
  };
5981
6237
  async function encodeContractCall(functionName, params) {
@@ -6249,7 +6505,10 @@ function parseDERSignature(sigHex) {
6249
6505
  }
6250
6506
  let offset = 0;
6251
6507
  if (raw.slice(offset, offset + 2) !== "30") {
6252
- return { r: raw.slice(0, 64).padStart(64, "0"), s: raw.slice(64).padStart(64, "0") };
6508
+ return {
6509
+ r: raw.slice(0, 64).padStart(64, "0"),
6510
+ s: raw.slice(64).padStart(64, "0")
6511
+ };
6253
6512
  }
6254
6513
  offset += 2;
6255
6514
  offset += 2;
@@ -6324,7 +6583,12 @@ var PipeInterface = class {
6324
6583
  const cmd = JSON.parse(nextLine);
6325
6584
  await this.handleCommand(cmd);
6326
6585
  } catch (err) {
6327
- this.emit({ type: "error", message: `Invalid input: ${err.message}` });
6586
+ const { message, code } = normalizeAgentError(err);
6587
+ this.emit({
6588
+ type: "error",
6589
+ message: `Invalid input: ${message}`,
6590
+ code: code === "UNKNOWN_ERROR" /* UNKNOWN_ERROR */ ? "INVALID_INPUT" /* INVALID_INPUT */ : code
6591
+ });
6328
6592
  }
6329
6593
  }
6330
6594
  processing = false;
@@ -6365,8 +6629,16 @@ var PipeInterface = class {
6365
6629
  onToolCall: (id, action, params) => {
6366
6630
  this.emit({ type: "tool_call", id, action, params, status: "running" });
6367
6631
  },
6368
- onToolResult: (id, action, success2, data, error2) => {
6369
- this.emit({ type: "tool_result", id, action, success: success2, data, error: error2 });
6632
+ onToolResult: (id, action, success2, data, error2, code) => {
6633
+ this.emit({
6634
+ type: "tool_result",
6635
+ id,
6636
+ action,
6637
+ success: success2,
6638
+ data,
6639
+ error: error2,
6640
+ ...!success2 && code ? { code } : {}
6641
+ });
6370
6642
  },
6371
6643
  onAssistantMessage: (content) => {
6372
6644
  this.emit({ type: "assistant", content });
@@ -6383,8 +6655,8 @@ var PipeInterface = class {
6383
6655
  explorer_url: explorerUrl
6384
6656
  });
6385
6657
  },
6386
- onError: (message) => {
6387
- this.emit({ type: "error", message });
6658
+ onError: (message, code) => {
6659
+ this.emit({ type: "error", message, code });
6388
6660
  },
6389
6661
  onDone: () => {
6390
6662
  this.emit({ type: "done" });
@@ -6392,13 +6664,17 @@ var PipeInterface = class {
6392
6664
  requestPassword: async () => {
6393
6665
  return new Promise((resolve) => {
6394
6666
  this.pendingPasswordResolve = resolve;
6395
- this.emit({ type: "error", message: "PASSWORD_REQUIRED" });
6667
+ this.emit({ type: "error", message: "PASSWORD_REQUIRED", code: "PASSWORD_REQUIRED" /* PASSWORD_REQUIRED */ });
6396
6668
  });
6397
6669
  },
6398
6670
  requestConfirmation: async (message) => {
6399
6671
  return new Promise((resolve) => {
6400
6672
  this.pendingConfirmResolve = resolve;
6401
- this.emit({ type: "error", message: `CONFIRMATION_REQUIRED: ${message}` });
6673
+ this.emit({
6674
+ type: "error",
6675
+ message: `CONFIRMATION_REQUIRED: ${message}`,
6676
+ code: "CONFIRMATION_REQUIRED" /* CONFIRMATION_REQUIRED */
6677
+ });
6402
6678
  });
6403
6679
  }
6404
6680
  };
@@ -6410,7 +6686,8 @@ var PipeInterface = class {
6410
6686
  try {
6411
6687
  await this.session.sendMessage(cmd.content, callbacks);
6412
6688
  } catch (err) {
6413
- this.emit({ type: "error", message: err.message });
6689
+ const { message, code } = normalizeAgentError(err);
6690
+ this.emit({ type: "error", message, code });
6414
6691
  this.emit({ type: "done" });
6415
6692
  }
6416
6693
  break;
@@ -6430,7 +6707,11 @@ var PipeInterface = class {
6430
6707
  break;
6431
6708
  }
6432
6709
  default:
6433
- this.emit({ type: "error", message: `Unknown command type: ${cmd.type}` });
6710
+ this.emit({
6711
+ type: "error",
6712
+ message: `Unknown command type: ${cmd.type}`,
6713
+ code: "INVALID_INPUT" /* INVALID_INPUT */
6714
+ });
6434
6715
  }
6435
6716
  }
6436
6717
  emit(event) {
@@ -6459,7 +6740,7 @@ var AgentSession = class {
6459
6740
  this.config = config;
6460
6741
  this.client = new AgentClient(config.backendUrl);
6461
6742
  this.client.verbose = !!config.verbose;
6462
- this.executor = new AgentExecutor(vault, !!config.verbose, vault.publicKeys.ecdsa);
6743
+ this.executor = new AgentExecutor(vault, !!config.verbose, vault.publicKeys.ecdsa, config.vultisig);
6463
6744
  this.publicKey = vault.publicKeys.ecdsa;
6464
6745
  if (config.password) {
6465
6746
  this.executor.setPassword(config.password);
@@ -6613,9 +6894,11 @@ var AgentSession = class {
6613
6894
  action_id: result.action_id,
6614
6895
  success: result.success,
6615
6896
  data: result.data || {},
6616
- error: result.error || ""
6897
+ error: result.error || "",
6898
+ ...!result.success && result.code ? { code: result.code } : {}
6617
6899
  };
6618
6900
  }
6901
+ let serverTxStoredFromStream = 0;
6619
6902
  const streamResult = await this.client.sendMessageStream(
6620
6903
  this.conversationId,
6621
6904
  request,
@@ -6636,36 +6919,25 @@ var AgentSession = class {
6636
6919
  ui.onSuggestions(suggestions);
6637
6920
  },
6638
6921
  onTxReady: (tx) => {
6639
- const txData = tx?.swap_tx || tx?.send_tx || tx?.tx;
6640
- if (txData?.status === "error" || txData?.error) {
6641
- if (this.config.verbose)
6642
- process.stderr.write(`[session] skipping error tx_ready: ${txData.error || "unknown error"}
6643
- `);
6644
- return;
6645
- }
6646
- this.executor.storeServerTransaction(tx);
6647
- if (this.config.password) {
6648
- this.executor.setPassword(this.config.password);
6922
+ if (this.executor.storeServerTransaction(tx)) {
6923
+ serverTxStoredFromStream++;
6924
+ if (this.config.password) {
6925
+ this.executor.setPassword(this.config.password);
6926
+ }
6649
6927
  }
6650
6928
  },
6651
6929
  onMessage: (_msg) => {
6652
6930
  },
6653
- onError: (error2) => {
6654
- ui.onError(error2);
6931
+ onError: (error2, code) => {
6932
+ ui.onError(error2, code);
6655
6933
  }
6656
6934
  },
6657
6935
  this.abortController?.signal
6658
6936
  );
6659
6937
  const responseText = streamResult.message?.content || streamResult.fullText || "";
6660
- const inlineActions = parseInlineToolCalls(responseText);
6661
- if (inlineActions.length > 0) {
6662
- const cleanText = responseText.replace(/<invoke\s+name="[^"]*">[\s\S]*?<\/invoke>/g, "").replace(/<\/?minimax:tool_call>/g, "").trim();
6663
- if (cleanText) {
6664
- ui.onAssistantMessage(cleanText);
6665
- }
6666
- streamResult.actions.push(...inlineActions);
6667
- } else if (responseText) {
6668
- ui.onAssistantMessage(responseText);
6938
+ const displayText = stripLeakedToolCallTags(responseText);
6939
+ if (displayText) {
6940
+ ui.onAssistantMessage(displayText);
6669
6941
  }
6670
6942
  const actions = streamResult.actions.filter((a) => a.type !== "sign_tx");
6671
6943
  if (actions.length > 0) {
@@ -6696,10 +6968,12 @@ var AgentSession = class {
6696
6968
  return;
6697
6969
  }
6698
6970
  }
6699
- if (streamResult.transactions.length > 0 && this.executor.hasPendingTransaction()) {
6971
+ if (serverTxStoredFromStream > 0) {
6700
6972
  if (this.config.verbose)
6701
- process.stderr.write(`[session] ${streamResult.transactions.length} tx_ready events, signing client-side
6702
- `);
6973
+ process.stderr.write(
6974
+ `[session] ${serverTxStoredFromStream} stored server tx from tx_ready, signing client-side
6975
+ `
6976
+ );
6703
6977
  const signAction = {
6704
6978
  id: `tx_sign_${Date.now()}`,
6705
6979
  type: "sign_tx",
@@ -6737,7 +7011,8 @@ var AgentSession = class {
6737
7011
  action: action.type,
6738
7012
  action_id: action.id,
6739
7013
  success: false,
6740
- error: "Password not provided"
7014
+ error: "Password not provided",
7015
+ code: "PASSWORD_REQUIRED" /* PASSWORD_REQUIRED */
6741
7016
  });
6742
7017
  continue;
6743
7018
  }
@@ -6746,7 +7021,7 @@ var AgentSession = class {
6746
7021
  ui.onToolCall(action.id, action.type, action.params);
6747
7022
  const result = await this.executor.executeAction(action);
6748
7023
  results.push(result);
6749
- ui.onToolResult(action.id, action.type, result.success, result.data, result.error);
7024
+ ui.onToolResult(action.id, action.type, result.success, result.data, result.error, result.code);
6750
7025
  if (action.type === "sign_tx" && result.success && result.data) {
6751
7026
  const txHash = result.data.tx_hash;
6752
7027
  const chain = result.data.chain;
@@ -6776,34 +7051,12 @@ var AgentSession = class {
6776
7051
  this.historyMessages = [];
6777
7052
  }
6778
7053
  };
6779
- function parseInlineToolCalls(text) {
6780
- const actions = [];
6781
- const invokeRegex = /<invoke\s+name="([^"]+)">([\s\S]*?)<\/invoke>/g;
6782
- let match;
6783
- while ((match = invokeRegex.exec(text)) !== null) {
6784
- const actionType = match[1];
6785
- const body = match[2];
6786
- const params = {};
6787
- const paramRegex = /<parameter\s+name="([^"]+)">([\s\S]*?)<\/parameter>/g;
6788
- let paramMatch;
6789
- while ((paramMatch = paramRegex.exec(body)) !== null) {
6790
- const key = paramMatch[1];
6791
- const value = paramMatch[2];
6792
- try {
6793
- params[key] = JSON.parse(value);
6794
- } catch {
6795
- params[key] = value;
6796
- }
6797
- }
6798
- actions.push({
6799
- id: `inline_${actionType}_${Date.now()}`,
6800
- type: actionType,
6801
- title: actionType,
6802
- params,
6803
- auto_execute: true
6804
- });
7054
+ function stripLeakedToolCallTags(text) {
7055
+ if (!text) return "";
7056
+ if (!/<invoke\s+name="[^"]*">/.test(text) && !text.includes("minimax:tool_call")) {
7057
+ return text;
6805
7058
  }
6806
- return actions;
7059
+ return text.replace(/<invoke\s+name="[^"]*">[\s\S]*?<\/invoke>/g, "").replace(/<\/?minimax:tool_call>/g, "").replace(/\n{3,}/g, "\n\n").trim();
6807
7060
  }
6808
7061
  function getTokenCachePath() {
6809
7062
  const dir = process.env.VULTISIG_CONFIG_DIR ?? join2(homedir2(), ".vultisig");
@@ -6979,7 +7232,7 @@ var ChatTUI = class {
6979
7232
  console.log(` ${chalk8.yellow("\u26A1")} ${chalk8.yellow(action)} ${chalk8.gray("...")}`);
6980
7233
  }
6981
7234
  },
6982
- onToolResult: (_id, action, success2, data, error2) => {
7235
+ onToolResult: (_id, action, success2, data, error2, code) => {
6983
7236
  if (success2) {
6984
7237
  if (this.verbose) {
6985
7238
  const summary = data ? summarizeData(data) : "";
@@ -6988,7 +7241,8 @@ var ChatTUI = class {
6988
7241
  console.log(` ${chalk8.green("\u2713")} ${chalk8.green(action)}`);
6989
7242
  }
6990
7243
  } else {
6991
- console.log(` ${chalk8.red("\u2717")} ${chalk8.red(action)}: ${chalk8.red(error2 || "failed")}`);
7244
+ const suffix = code && this.verbose ? chalk8.gray(` (${code})`) : "";
7245
+ console.log(` ${chalk8.red("\u2717")} ${chalk8.red(action)}: ${chalk8.red(error2 || "failed")}${suffix}`);
6992
7246
  }
6993
7247
  },
6994
7248
  onAssistantMessage: (content) => {
@@ -7016,12 +7270,13 @@ var ChatTUI = class {
7016
7270
  console.log(` ${chalk8.blue.underline(explorerUrl)}`);
7017
7271
  }
7018
7272
  },
7019
- onError: (message) => {
7273
+ onError: (message, code) => {
7020
7274
  if (this.isStreaming) {
7021
7275
  process.stdout.write("\n");
7022
7276
  this.isStreaming = false;
7023
7277
  }
7024
- console.log(` ${chalk8.red("Error")}: ${message}`);
7278
+ const suffix = this.verbose ? chalk8.gray(` (${code})`) : "";
7279
+ console.log(` ${chalk8.red("Error")}: ${message}${suffix}`);
7025
7280
  },
7026
7281
  onDone: () => {
7027
7282
  if (this.isStreaming) {
@@ -7214,6 +7469,7 @@ async function executeAgent(ctx2, options) {
7214
7469
  const config = {
7215
7470
  backendUrl: options.backendUrl || process.env.VULTISIG_AGENT_URL || "https://abe.vultisig.com",
7216
7471
  vaultName: vault.name,
7472
+ vultisig: ctx2.sdk,
7217
7473
  password: options.password,
7218
7474
  viaAgent: options.viaAgent,
7219
7475
  sessionId: options.sessionId,
@@ -7229,7 +7485,8 @@ async function executeAgent(ctx2, options) {
7229
7485
  const addresses = session.getVaultAddresses();
7230
7486
  await pipe.start(vault.name, addresses);
7231
7487
  } catch (err) {
7232
- process.stdout.write(JSON.stringify({ type: "error", message: err.message }) + "\n");
7488
+ const { code, message } = normalizeAgentError(err);
7489
+ process.stdout.write(JSON.stringify({ type: "error", message, code }) + "\n");
7233
7490
  process.exit(1);
7234
7491
  }
7235
7492
  } else {
@@ -7239,7 +7496,8 @@ async function executeAgent(ctx2, options) {
7239
7496
  await session.initialize(callbacks);
7240
7497
  await tui.start();
7241
7498
  } catch (err) {
7242
- console.error(`Agent error: ${err.message}`);
7499
+ const { message } = normalizeAgentError(err);
7500
+ console.error(`Agent error: ${message}`);
7243
7501
  process.exit(1);
7244
7502
  }
7245
7503
  }
@@ -7255,6 +7513,7 @@ async function executeAgentAsk(ctx2, message, options) {
7255
7513
  const config = {
7256
7514
  backendUrl: options.backendUrl || process.env.VULTISIG_AGENT_URL || "https://abe.vultisig.com",
7257
7515
  vaultName: vault.name,
7516
+ vultisig: ctx2.sdk,
7258
7517
  password: options.password,
7259
7518
  sessionId: options.session,
7260
7519
  verbose: options.verbose,
@@ -7293,10 +7552,11 @@ tx:${tx.chain}:${tx.hash}
7293
7552
  }
7294
7553
  }
7295
7554
  } catch (err) {
7555
+ const { code, message: message2 } = normalizeAgentError(err);
7296
7556
  if (options.json) {
7297
- process.stdout.write(JSON.stringify({ error: err.message }) + "\n");
7557
+ process.stdout.write(JSON.stringify({ error: message2, code }) + "\n");
7298
7558
  } else {
7299
- process.stderr.write(`Error: ${err.message}
7559
+ process.stderr.write(`Error: ${message2} [${code}]
7300
7560
  `);
7301
7561
  }
7302
7562
  process.exit(1);
@@ -7759,7 +8019,7 @@ var EventBuffer = class {
7759
8019
  };
7760
8020
 
7761
8021
  // src/interactive/session.ts
7762
- import { fiatCurrencies as fiatCurrencies3 } from "@vultisig/sdk";
8022
+ import { fiatCurrencies as fiatCurrencies4 } from "@vultisig/sdk";
7763
8023
  import chalk12 from "chalk";
7764
8024
  import ora3 from "ora";
7765
8025
  import * as readline3 from "readline";
@@ -8577,9 +8837,9 @@ Error: ${error2.message}`));
8577
8837
  i++;
8578
8838
  }
8579
8839
  }
8580
- if (!fiatCurrencies3.includes(currency)) {
8840
+ if (!fiatCurrencies4.includes(currency)) {
8581
8841
  console.log(chalk12.red(`Invalid currency: ${currency}`));
8582
- console.log(chalk12.yellow(`Supported currencies: ${fiatCurrencies3.join(", ")}`));
8842
+ console.log(chalk12.yellow(`Supported currencies: ${fiatCurrencies4.join(", ")}`));
8583
8843
  return;
8584
8844
  }
8585
8845
  const raw = args.includes("--raw");
@@ -8828,7 +9088,7 @@ var cachedVersion = null;
8828
9088
  function getVersion() {
8829
9089
  if (cachedVersion) return cachedVersion;
8830
9090
  if (true) {
8831
- cachedVersion = "0.15.0";
9091
+ cachedVersion = "0.15.4";
8832
9092
  return cachedVersion;
8833
9093
  }
8834
9094
  try {
@@ -9323,7 +9583,7 @@ async function init(vaultOverride, unlockPassword, passwordTTL) {
9323
9583
  }
9324
9584
  const globalOptions = program.opts();
9325
9585
  const serverEndpoints = resolveServerEndpoints(globalOptions);
9326
- const sdk = new Vultisig7({
9586
+ const sdk = new Vultisig6({
9327
9587
  onPasswordRequired: createPasswordCallback(),
9328
9588
  ...serverEndpoints ? { serverEndpoints } : {},
9329
9589
  ...passwordTTL !== void 0 ? { passwordCache: { defaultTTL: passwordTTL } } : {}
@@ -9940,7 +10200,7 @@ program.command("update").description("Check for updates and show update command
9940
10200
  setupCompletionCommand(program);
9941
10201
  async function startInteractiveMode() {
9942
10202
  const serverEndpoints = resolveServerEndpoints(parseServerEndpointOverridesFromArgv(process.argv.slice(2)));
9943
- const sdk = new Vultisig7({
10203
+ const sdk = new Vultisig6({
9944
10204
  onPasswordRequired: createPasswordCallback(),
9945
10205
  ...serverEndpoints ? { serverEndpoints } : {}
9946
10206
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vultisig/cli",
3
- "version": "0.15.0",
3
+ "version": "0.15.4",
4
4
  "description": "Command-line wallet for Vultisig - multi-chain MPC wallet management",
5
5
  "type": "module",
6
6
  "bin": {
@@ -55,7 +55,7 @@
55
55
  "@cosmjs/stargate": "^0.38.1",
56
56
  "@noble/hashes": "^2.0.1",
57
57
  "@vultisig/rujira": "^10.0.0",
58
- "@vultisig/sdk": "^0.15.1",
58
+ "@vultisig/sdk": "^0.15.4",
59
59
  "chalk": "^5.6.2",
60
60
  "cli-table3": "^0.6.5",
61
61
  "commander": "^14.0.3",