@vultisig/cli 0.15.2 → 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,19 @@
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
+
3
17
  ## 0.15.2
4
18
 
5
19
  ### 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
@@ -4226,6 +4226,122 @@ function displayDiscountTier(tierInfo) {
4226
4226
  import chalk9 from "chalk";
4227
4227
  import Table from "cli-table3";
4228
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
+
4229
4345
  // src/agent/ask.ts
4230
4346
  var AskInterface = class {
4231
4347
  session;
@@ -4252,10 +4368,10 @@ var AskInterface = class {
4252
4368
  `);
4253
4369
  }
4254
4370
  },
4255
- onToolResult: (_id, action, success2, data, error2) => {
4256
- 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 });
4257
4373
  if (this.verbose) {
4258
- const status = success2 ? "ok" : `error: ${error2}`;
4374
+ const status = success2 ? "ok" : `error: ${error2}${code ? ` [${code}]` : ""}`;
4259
4375
  process.stderr.write(`[tool] ${action}: ${status}
4260
4376
  `);
4261
4377
  }
@@ -4274,8 +4390,8 @@ var AskInterface = class {
4274
4390
  `);
4275
4391
  }
4276
4392
  },
4277
- onError: (message) => {
4278
- process.stderr.write(`[error] ${message}
4393
+ onError: (message, code) => {
4394
+ process.stderr.write(`[error] ${message} [${code}]
4279
4395
  `);
4280
4396
  },
4281
4397
  onDone: () => {
@@ -4406,6 +4522,17 @@ function padTo32Bytes(buf) {
4406
4522
  }
4407
4523
 
4408
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
+ }
4409
4536
  var AgentClient = class {
4410
4537
  baseUrl;
4411
4538
  authToken = null;
@@ -4460,7 +4587,9 @@ var AgentClient = class {
4460
4587
  return this.post(`/agent/conversations/${conversationId}`, req);
4461
4588
  }
4462
4589
  async deleteConversation(conversationId, publicKey) {
4463
- await this.delete(`/agent/conversations/${conversationId}`, { public_key: publicKey });
4590
+ await this.delete(`/agent/conversations/${conversationId}`, {
4591
+ public_key: publicKey
4592
+ });
4464
4593
  }
4465
4594
  // ============================================================================
4466
4595
  // Messages - JSON mode
@@ -4581,9 +4710,12 @@ var AgentClient = class {
4581
4710
  result.message = parsed.message || parsed;
4582
4711
  callbacks.onMessage?.(result.message);
4583
4712
  break;
4584
- case "error":
4585
- 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);
4586
4717
  break;
4718
+ }
4587
4719
  case "done":
4588
4720
  break;
4589
4721
  }
@@ -5092,11 +5224,13 @@ var AgentExecutor = class {
5092
5224
  data
5093
5225
  };
5094
5226
  } catch (err) {
5227
+ const { code, message } = normalizeAgentError(err);
5095
5228
  return {
5096
5229
  action: action.type,
5097
5230
  action_id: action.id,
5098
5231
  success: false,
5099
- error: err.message || String(err)
5232
+ error: message,
5233
+ code
5100
5234
  };
5101
5235
  }
5102
5236
  }
@@ -6449,7 +6583,12 @@ var PipeInterface = class {
6449
6583
  const cmd = JSON.parse(nextLine);
6450
6584
  await this.handleCommand(cmd);
6451
6585
  } catch (err) {
6452
- 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
+ });
6453
6592
  }
6454
6593
  }
6455
6594
  processing = false;
@@ -6490,8 +6629,16 @@ var PipeInterface = class {
6490
6629
  onToolCall: (id, action, params) => {
6491
6630
  this.emit({ type: "tool_call", id, action, params, status: "running" });
6492
6631
  },
6493
- onToolResult: (id, action, success2, data, error2) => {
6494
- 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
+ });
6495
6642
  },
6496
6643
  onAssistantMessage: (content) => {
6497
6644
  this.emit({ type: "assistant", content });
@@ -6508,8 +6655,8 @@ var PipeInterface = class {
6508
6655
  explorer_url: explorerUrl
6509
6656
  });
6510
6657
  },
6511
- onError: (message) => {
6512
- this.emit({ type: "error", message });
6658
+ onError: (message, code) => {
6659
+ this.emit({ type: "error", message, code });
6513
6660
  },
6514
6661
  onDone: () => {
6515
6662
  this.emit({ type: "done" });
@@ -6517,13 +6664,17 @@ var PipeInterface = class {
6517
6664
  requestPassword: async () => {
6518
6665
  return new Promise((resolve) => {
6519
6666
  this.pendingPasswordResolve = resolve;
6520
- this.emit({ type: "error", message: "PASSWORD_REQUIRED" });
6667
+ this.emit({ type: "error", message: "PASSWORD_REQUIRED", code: "PASSWORD_REQUIRED" /* PASSWORD_REQUIRED */ });
6521
6668
  });
6522
6669
  },
6523
6670
  requestConfirmation: async (message) => {
6524
6671
  return new Promise((resolve) => {
6525
6672
  this.pendingConfirmResolve = resolve;
6526
- 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
+ });
6527
6678
  });
6528
6679
  }
6529
6680
  };
@@ -6535,7 +6686,8 @@ var PipeInterface = class {
6535
6686
  try {
6536
6687
  await this.session.sendMessage(cmd.content, callbacks);
6537
6688
  } catch (err) {
6538
- this.emit({ type: "error", message: err.message });
6689
+ const { message, code } = normalizeAgentError(err);
6690
+ this.emit({ type: "error", message, code });
6539
6691
  this.emit({ type: "done" });
6540
6692
  }
6541
6693
  break;
@@ -6555,7 +6707,11 @@ var PipeInterface = class {
6555
6707
  break;
6556
6708
  }
6557
6709
  default:
6558
- 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
+ });
6559
6715
  }
6560
6716
  }
6561
6717
  emit(event) {
@@ -6738,7 +6894,8 @@ var AgentSession = class {
6738
6894
  action_id: result.action_id,
6739
6895
  success: result.success,
6740
6896
  data: result.data || {},
6741
- error: result.error || ""
6897
+ error: result.error || "",
6898
+ ...!result.success && result.code ? { code: result.code } : {}
6742
6899
  };
6743
6900
  }
6744
6901
  let serverTxStoredFromStream = 0;
@@ -6771,22 +6928,16 @@ var AgentSession = class {
6771
6928
  },
6772
6929
  onMessage: (_msg) => {
6773
6930
  },
6774
- onError: (error2) => {
6775
- ui.onError(error2);
6931
+ onError: (error2, code) => {
6932
+ ui.onError(error2, code);
6776
6933
  }
6777
6934
  },
6778
6935
  this.abortController?.signal
6779
6936
  );
6780
6937
  const responseText = streamResult.message?.content || streamResult.fullText || "";
6781
- const inlineActions = parseInlineToolCalls(responseText);
6782
- if (inlineActions.length > 0) {
6783
- const cleanText = responseText.replace(/<invoke\s+name="[^"]*">[\s\S]*?<\/invoke>/g, "").replace(/<\/?minimax:tool_call>/g, "").trim();
6784
- if (cleanText) {
6785
- ui.onAssistantMessage(cleanText);
6786
- }
6787
- streamResult.actions.push(...inlineActions);
6788
- } else if (responseText) {
6789
- ui.onAssistantMessage(responseText);
6938
+ const displayText = stripLeakedToolCallTags(responseText);
6939
+ if (displayText) {
6940
+ ui.onAssistantMessage(displayText);
6790
6941
  }
6791
6942
  const actions = streamResult.actions.filter((a) => a.type !== "sign_tx");
6792
6943
  if (actions.length > 0) {
@@ -6860,7 +7011,8 @@ var AgentSession = class {
6860
7011
  action: action.type,
6861
7012
  action_id: action.id,
6862
7013
  success: false,
6863
- error: "Password not provided"
7014
+ error: "Password not provided",
7015
+ code: "PASSWORD_REQUIRED" /* PASSWORD_REQUIRED */
6864
7016
  });
6865
7017
  continue;
6866
7018
  }
@@ -6869,7 +7021,7 @@ var AgentSession = class {
6869
7021
  ui.onToolCall(action.id, action.type, action.params);
6870
7022
  const result = await this.executor.executeAction(action);
6871
7023
  results.push(result);
6872
- 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);
6873
7025
  if (action.type === "sign_tx" && result.success && result.data) {
6874
7026
  const txHash = result.data.tx_hash;
6875
7027
  const chain = result.data.chain;
@@ -6899,34 +7051,12 @@ var AgentSession = class {
6899
7051
  this.historyMessages = [];
6900
7052
  }
6901
7053
  };
6902
- function parseInlineToolCalls(text) {
6903
- const actions = [];
6904
- const invokeRegex = /<invoke\s+name="([^"]+)">([\s\S]*?)<\/invoke>/g;
6905
- let match;
6906
- while ((match = invokeRegex.exec(text)) !== null) {
6907
- const actionType = match[1];
6908
- const body = match[2];
6909
- const params = {};
6910
- const paramRegex = /<parameter\s+name="([^"]+)">([\s\S]*?)<\/parameter>/g;
6911
- let paramMatch;
6912
- while ((paramMatch = paramRegex.exec(body)) !== null) {
6913
- const key = paramMatch[1];
6914
- const value = paramMatch[2];
6915
- try {
6916
- params[key] = JSON.parse(value);
6917
- } catch {
6918
- params[key] = value;
6919
- }
6920
- }
6921
- actions.push({
6922
- id: `inline_${actionType}_${Date.now()}`,
6923
- type: actionType,
6924
- title: actionType,
6925
- params,
6926
- auto_execute: true
6927
- });
7054
+ function stripLeakedToolCallTags(text) {
7055
+ if (!text) return "";
7056
+ if (!/<invoke\s+name="[^"]*">/.test(text) && !text.includes("minimax:tool_call")) {
7057
+ return text;
6928
7058
  }
6929
- 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();
6930
7060
  }
6931
7061
  function getTokenCachePath() {
6932
7062
  const dir = process.env.VULTISIG_CONFIG_DIR ?? join2(homedir2(), ".vultisig");
@@ -7102,7 +7232,7 @@ var ChatTUI = class {
7102
7232
  console.log(` ${chalk8.yellow("\u26A1")} ${chalk8.yellow(action)} ${chalk8.gray("...")}`);
7103
7233
  }
7104
7234
  },
7105
- onToolResult: (_id, action, success2, data, error2) => {
7235
+ onToolResult: (_id, action, success2, data, error2, code) => {
7106
7236
  if (success2) {
7107
7237
  if (this.verbose) {
7108
7238
  const summary = data ? summarizeData(data) : "";
@@ -7111,7 +7241,8 @@ var ChatTUI = class {
7111
7241
  console.log(` ${chalk8.green("\u2713")} ${chalk8.green(action)}`);
7112
7242
  }
7113
7243
  } else {
7114
- 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}`);
7115
7246
  }
7116
7247
  },
7117
7248
  onAssistantMessage: (content) => {
@@ -7139,12 +7270,13 @@ var ChatTUI = class {
7139
7270
  console.log(` ${chalk8.blue.underline(explorerUrl)}`);
7140
7271
  }
7141
7272
  },
7142
- onError: (message) => {
7273
+ onError: (message, code) => {
7143
7274
  if (this.isStreaming) {
7144
7275
  process.stdout.write("\n");
7145
7276
  this.isStreaming = false;
7146
7277
  }
7147
- console.log(` ${chalk8.red("Error")}: ${message}`);
7278
+ const suffix = this.verbose ? chalk8.gray(` (${code})`) : "";
7279
+ console.log(` ${chalk8.red("Error")}: ${message}${suffix}`);
7148
7280
  },
7149
7281
  onDone: () => {
7150
7282
  if (this.isStreaming) {
@@ -7353,7 +7485,8 @@ async function executeAgent(ctx2, options) {
7353
7485
  const addresses = session.getVaultAddresses();
7354
7486
  await pipe.start(vault.name, addresses);
7355
7487
  } catch (err) {
7356
- 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");
7357
7490
  process.exit(1);
7358
7491
  }
7359
7492
  } else {
@@ -7363,7 +7496,8 @@ async function executeAgent(ctx2, options) {
7363
7496
  await session.initialize(callbacks);
7364
7497
  await tui.start();
7365
7498
  } catch (err) {
7366
- console.error(`Agent error: ${err.message}`);
7499
+ const { message } = normalizeAgentError(err);
7500
+ console.error(`Agent error: ${message}`);
7367
7501
  process.exit(1);
7368
7502
  }
7369
7503
  }
@@ -7418,10 +7552,11 @@ tx:${tx.chain}:${tx.hash}
7418
7552
  }
7419
7553
  }
7420
7554
  } catch (err) {
7555
+ const { code, message: message2 } = normalizeAgentError(err);
7421
7556
  if (options.json) {
7422
- process.stdout.write(JSON.stringify({ error: err.message }) + "\n");
7557
+ process.stdout.write(JSON.stringify({ error: message2, code }) + "\n");
7423
7558
  } else {
7424
- process.stderr.write(`Error: ${err.message}
7559
+ process.stderr.write(`Error: ${message2} [${code}]
7425
7560
  `);
7426
7561
  }
7427
7562
  process.exit(1);
@@ -8953,7 +9088,7 @@ var cachedVersion = null;
8953
9088
  function getVersion() {
8954
9089
  if (cachedVersion) return cachedVersion;
8955
9090
  if (true) {
8956
- cachedVersion = "0.15.2";
9091
+ cachedVersion = "0.15.4";
8957
9092
  return cachedVersion;
8958
9093
  }
8959
9094
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vultisig/cli",
3
- "version": "0.15.2",
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.3",
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",