@vultisig/cli 2.19.18 → 2.21.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 (4) hide show
  1. package/CHANGELOG.md +169 -0
  2. package/README.md +200 -85
  3. package/dist/index.js +2236 -2016
  4. package/package.json +8 -6
package/README.md CHANGED
@@ -370,24 +370,24 @@ Swap quotes and previews show your VULT discount tier when affiliate fees are ap
370
370
 
371
371
  #### Transaction Status
372
372
 
373
- Check whether a transaction has confirmed on-chain. By default, polls every 5 seconds until the transaction reaches a final state (success or error):
373
+ Check whether a transaction has confirmed on-chain. The CLI reports `pending`, `not_found`, `confirmed`, or `failed`. A recently broadcast hash may briefly be `not_found`, so the default mode polls every 5 seconds for up to 120 seconds. Use `--no-wait` for one read:
374
374
 
375
375
  ```bash
376
376
  # Poll until confirmed (default)
377
- vultisig tx-status ethereum 0x9f8e7d6c...
377
+ vultisig tx-status --chain Ethereum --tx-hash 0x9f8e7d6c...
378
378
 
379
379
  # Check current status without polling
380
- vultisig tx-status ethereum 0x9f8e7d6c... --no-wait
380
+ vultisig tx-status --chain Ethereum --tx-hash 0x9f8e7d6c... --no-wait
381
381
 
382
382
  # JSON output
383
- vultisig tx-status ethereum 0x9f8e7d6c... -o json
383
+ vultisig --output json tx-status --chain Ethereum --tx-hash 0x9f8e7d6c... --no-wait
384
384
  ```
385
385
 
386
386
  **Output:**
387
387
 
388
388
  ```
389
- ✓ Transaction status: success
390
- Status: success
389
+ ✓ Transaction status: confirmed
390
+ Status: confirmed
391
391
  Fee: 0.00042 ETH
392
392
  Explorer: https://etherscan.io/tx/0x9f8e7d6c...
393
393
  ```
@@ -396,18 +396,26 @@ Explorer: https://etherscan.io/tx/0x9f8e7d6c...
396
396
 
397
397
  ```json
398
398
  {
399
- "chain": "ethereum",
400
- "txHash": "0x9f8e7d6c...",
401
- "status": "success",
402
- "receipt": {
403
- "feeAmount": "420000000000000",
404
- "feeDecimals": 18,
405
- "feeTicker": "ETH"
406
- },
407
- "explorerUrl": "https://etherscan.io/tx/0x9f8e7d6c..."
399
+ "success": true,
400
+ "v": 1,
401
+ "data": {
402
+ "chain": "Ethereum",
403
+ "txHash": "0x9f8e7d6c...",
404
+ "status": "confirmed",
405
+ "receipt": {
406
+ "feeAmount": "420000000000000",
407
+ "feeDecimals": 18,
408
+ "feeTicker": "ETH"
409
+ },
410
+ "explorerUrl": "https://etherscan.io/tx/0x9f8e7d6c..."
411
+ }
408
412
  }
409
413
  ```
410
414
 
415
+ A malformed hash fails before vault access or RPC with exit code `4`. JSON output uses error code `INVALID_HASH` and includes `error.context.status: "invalid_hash"`. A well-formed hash unknown to the node reports `not_found` in `--no-wait` mode; default polling exits `5` with `TX_NOT_FOUND` if it remains unseen for the wait budget. A known, unconfirmed transaction remains `pending`; if it is still `pending` when the wait budget is exhausted, default polling exits `3` with `TX_STATUS_TIMEOUT` (retryable) rather than reporting a false terminal status.
416
+
417
+ EVM RPCs can distinguish a missing receipt from a hash the node does not know, so they report `not_found` explicitly. Some non-EVM providers do not distinguish an absent transaction from a failed lookup; those chains conservatively remain `pending` with an unknown-presence signal, and default CLI polling is still bounded by `--timeout`.
418
+
411
419
  #### Signing Arbitrary Bytes
412
420
 
413
421
  Sign pre-hashed data for externally constructed transactions:
@@ -647,18 +655,61 @@ JSON output for two-step create:
647
655
 
648
656
  Send a single natural-language message and get a structured response. Designed for AI-to-AI communication.
649
657
 
650
- **Password resolution:** `agent ask` unlocks the vault from the keyring/env chain before prompting, so a headless operator never has to put the password on argv. Set it up **once** — store it in the OS keyring with `vsig auth setup` (recommended), or export `VAULT_PASSWORD` (or `VAULT_PASSWORDS="VaultName:pw ..."` for multiple vaults) — then run `agent ask` with **no** `--password`. The `--password` flag still works as a fallback but is discouraged: it exposes the secret to `ps` and shell history, and triggers a stderr warning.
658
+ #### Headless Password and Credential Chain
659
+
660
+ `agent ask` resolves the vault password before prompting, so automation does not need to put a secret on argv. The lookup order is:
661
+
662
+ 1. In-memory cache (an explicit `--password` or a password already resolved in this process)
663
+ 2. Stored credentials from `vsig auth setup` (OS keyring, or the encrypted-file backend)
664
+ 3. `VAULT_PASSWORDS` by vault name, then by vault ID
665
+ 4. `VAULT_PASSWORD`, then its namespaced alias `VULTISIG_PASSWORD`
666
+ 5. Interactive prompt, or an error in non-interactive mode
667
+
668
+ The five password-related environment variables have distinct roles:
669
+
670
+ | Variable | Purpose |
671
+ | --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
672
+ | `VAULT_PASSWORD` | Single fallback vault/server-signing password. It is also the server password required by `auth setup --non-interactive`. |
673
+ | `VULTISIG_PASSWORD` | Namespaced alias for the single-password fallback during normal vault unlock. `auth setup` still requires `VAULT_PASSWORD`. |
674
+ | `VAULT_PASSWORDS` | Per-vault passwords keyed by vault name or ID. Use the JSON form for names containing spaces. |
675
+ | `VAULT_DECRYPT_PASSWORD` | Password for decrypting an encrypted `.vult` backup during `auth setup`; it is not part of the normal signing-password lookup chain. |
676
+ | `VULTISIG_CREDENTIALS_PASSPHRASE` | Passphrase for the AES-256-GCM `credentials.enc` backend used instead of an OS keyring in Docker/CI. It must be set again when later commands read that file. |
677
+
678
+ `VAULT_PASSWORDS` accepts a JSON object (recommended) or the legacy whitespace-separated form:
651
679
 
652
680
  ```bash
653
- # Recommended: store the password once, then no --password on each call
654
- vsig auth setup # stores in the OS keyring
655
- # or: export VAULT_PASSWORD="..."
681
+ # Unambiguous: supports spaces and other punctuation in vault names
682
+ export VAULT_PASSWORDS='{"Vultisig Cluster #1":"pw","vault-id":"other-pw"}'
656
683
 
657
- # Simple query (password resolved from keyring/env)
658
- vultisig agent ask "What is my ETH balance?"
684
+ # Backward-compatible for space-free keys; passwords may contain colons
685
+ export VAULT_PASSWORDS='MyVault:pw vault-id:other:pw'
686
+ ```
687
+
688
+ Vault-ID keys are the most deterministic choice. If a JSON-looking value is malformed, the CLI warns on stderr and falls back to legacy parsing.
689
+
690
+ For a keychain-less container, provide all setup secrets once, persist the config directory, then remove the vault passwords from the environment. Keep the credentials-file passphrase available to later CLI processes:
691
+
692
+ ```bash
693
+ export VULTISIG_CONFIG_DIR=/var/lib/vultisig
694
+ export VAULT_DECRYPT_PASSWORD='backup-file-password'
695
+ export VAULT_PASSWORD='server-signing-password'
696
+ export VULTISIG_CREDENTIALS_PASSPHRASE='credentials-file-passphrase'
697
+
698
+ vsig auth setup --non-interactive --vault-file /run/secrets/vault.vult
699
+
700
+ # Setup stored both passwords in $VULTISIG_CONFIG_DIR/credentials.enc (mode 0600).
701
+ unset VAULT_DECRYPT_PASSWORD VAULT_PASSWORD
702
+ vsig auth status
659
703
 
660
- # Execute a transaction
661
- vultisig agent ask "Send 0.01 ETH to 0x742d..."
704
+ # No vault password on argv or in the environment. --yes authorizes signing/broadcast.
705
+ vsig agent ask 'Send 0.01 ETH to 0x742d...' --yes
706
+ ```
707
+
708
+ Omit `VAULT_DECRYPT_PASSWORD` when the `.vult` file is not encrypted. Mount `VULTISIG_CONFIG_DIR` persistently and provide the same `VULTISIG_CREDENTIALS_PASSPHRASE` to each new container. The `--password` flag remains available as a fallback, but it exposes the secret to `ps` and shell history and emits a stderr warning.
709
+
710
+ ```bash
711
+ # Simple query (password resolved from stored credentials or environment)
712
+ vultisig agent ask "What is my ETH balance?"
662
713
 
663
714
  # Continue a conversation (multi-turn)
664
715
  vultisig agent ask "Now swap it to USDC" --session abc123
@@ -668,8 +719,13 @@ vultisig agent ask "Check my portfolio" --json
668
719
 
669
720
  # Fallback only — exposes the secret to `ps`/shell history (emits a warning)
670
721
  vultisig agent ask "What is my ETH balance?" --password "$VAULT_PASSWORD"
722
+
723
+ # Signing does not authorize backend order submission unless this is also set
724
+ vultisig agent ask "Place the order" --yes --allow-auto-submit
671
725
  ```
672
726
 
727
+ `--yes` authorizes unattended signing and transaction broadcast. It does not authorize the backend to submit a signed Polymarket order: that separate behavior is fail-closed unless `--allow-auto-submit` is present.
728
+
673
729
  **Text output (default):**
674
730
 
675
731
  ```
@@ -685,41 +741,71 @@ explorer:https://etherscan.io/tx/0x9f8e7d6c...
685
741
 
686
742
  ```json
687
743
  {
688
- "session_id": "abc123-def456",
689
- "response": "Your ETH balance is 1.5 ETH ($3,750.00 USD).",
690
- "tool_calls": [
691
- {
692
- "action": "get_balances",
693
- "success": true,
694
- "data": {
695
- "balances": [
696
- {
697
- "chain": "Ethereum",
698
- "symbol": "ETH",
699
- "amount": "1.5",
700
- "decimals": 18,
701
- "raw_amount": "1500000000000000000"
702
- }
703
- ]
744
+ "success": true,
745
+ "v": 1,
746
+ "data": {
747
+ "conversation_id": "abc123-def456",
748
+ "session_id": "abc123-def456",
749
+ "response": "Your ETH balance is 1.5 ETH ($3,750.00 USD).",
750
+ "tool_calls": [
751
+ {
752
+ "id": "tool-call-1",
753
+ "action": "get_balances",
754
+ "success": true,
755
+ "data": { "balances": [] }
704
756
  }
705
- }
706
- ],
707
- "transactions": [
708
- {
709
- "hash": "0x9f8e7d6c...",
710
- "chain": "ethereum",
711
- "explorerUrl": "https://etherscan.io/tx/0x9f8e7d6c..."
712
- }
713
- ]
757
+ ],
758
+ "transactions": [],
759
+ "warnings": [
760
+ {
761
+ "code": "PROTOCOL_DRIFT",
762
+ "message": "Ignored 1 unknown SSE frame: data-future-critical",
763
+ "count": 1,
764
+ "eventTypes": ["data-future-critical"]
765
+ }
766
+ ],
767
+ "outcome": { "kind": "success" }
768
+ }
714
769
  }
715
770
  ```
716
771
 
717
- 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):
772
+ `warnings` is omitted when empty, and is **`--verbose`-only**: `PROTOCOL_DRIFT` is a debugging aid, not a machine contract. The backend's V1 wire evolves forward-compatibly — unknown `data-*` card kinds are expected against a newer backend and are tolerated silently — so a warning emitted by default would fire on healthy turns. Run with `--verbose` to see which frame types a turn carried that this CLI does not route.
773
+
774
+ Failures use the same v1 envelope with `success:false` and a stable `error.code`. This includes
775
+ failed/declined signing and typed blocked/refusal/error turn endings; their partial turn data remains under `data`.
776
+ If `--session` cannot be resumed, ask mode exits `5` before sending the message and does not fall back to a fresh
777
+ conversation.
778
+ If a transaction hash has already been submitted and a later backend outcome/error prevents the overall request
779
+ from completing, the CLI exits `13` with `BROADCAST_COMMITTED`. This is deliberately **not** overall success:
780
+ an approval or other first leg may have landed while a swap or follow-up step did not. Inspect every hash and do
781
+ not blindly retry the original request.
718
782
 
719
783
  ```json
720
784
  {
721
- "error": "Agent backend unreachable at https://example.invalid",
722
- "code": "BACKEND_UNREACHABLE"
785
+ "success": false,
786
+ "v": 1,
787
+ "error": {
788
+ "message": "A transaction was broadcast, but the overall agent request may be incomplete. Inspect the transaction status before continuing.",
789
+ "code": "BROADCAST_COMMITTED",
790
+ "conversation_id": "abc123-def456"
791
+ },
792
+ "data": {
793
+ "transactions": [
794
+ {
795
+ "hash": "0x9f8e7d6c...",
796
+ "chain": "ethereum",
797
+ "status": "broadcast",
798
+ "explorerUrl": "https://etherscan.io/tx/0x9f8e7d6c..."
799
+ }
800
+ ],
801
+ "tool_calls": [],
802
+ "response": "",
803
+ "outcome": { "kind": "error", "code": "follow_up_failed" },
804
+ "original_error": {
805
+ "message": "Confirmation indexer failed after broadcast",
806
+ "code": "TRANSACTION_FAILED"
807
+ }
808
+ }
723
809
  }
724
810
  ```
725
811
 
@@ -729,31 +815,41 @@ Each entry in `tool_calls` may include `code` when `success` is false (same valu
729
815
 
730
816
  Orchestrators should branch on `code`. The message in `error` / `message` stays human-readable and may change between releases.
731
817
 
732
- | Code | Typical meaning |
733
- | ------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
734
- | `BACKEND_UNREACHABLE` | Agent health check failed or backend not responding |
735
- | `AUTH_FAILED` | Auth/token failure, HTTP 401/403, or wrong vault password |
736
- | `VAULT_LOCKED` | Encrypted vault needs unlock (password) |
737
- | `PASSWORD_REQUIRED` | Password was not supplied when required (e.g. pipe mode or signing) |
738
- | `CONFIRMATION_REQUIRED` | User confirmation needed (pipe mode; message prefix `CONFIRMATION_REQUIRED:`) |
739
- | `ACTION_NOT_IMPLEMENTED` | Local executor does not implement this action type |
740
- | `INVALID_INPUT` | Bad parameters, unknown chain, malformed NDJSON input, etc. |
741
- | `NETWORK_ERROR` | RPC/fetch connectivity (includes many SDK `VaultError` network cases) |
742
- | `TIMEOUT` | Deadline exceeded, or abort where the message indicates a timeout |
743
- | `TRANSACTION_FAILED` | Build/broadcast/gas errors mapped from the SDK |
744
- | `SIGNING_FAILED` | MPC/signing failed |
745
- | `SESSION_NOT_INITIALIZED` | Internal session state error |
746
- | `UNKNOWN_ERROR` | Unclassified failure (default for opaque SSE `error` events). Plain `AbortError` without “timeout” in the message maps here. |
818
+ | Code | Typical meaning |
819
+ | --------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
820
+ | `BACKEND_UNREACHABLE` | Agent health check failed or backend not responding |
821
+ | `AUTH_FAILED` | Auth/token failure, HTTP 401/403, or wrong vault password |
822
+ | `VAULT_LOCKED` | Encrypted vault needs unlock (password) |
823
+ | `PASSWORD_REQUIRED` | Password was not supplied when required (e.g. pipe mode or signing) |
824
+ | `CONFIRMATION_REQUIRED` | User confirmation needed (pipe mode; message prefix `CONFIRMATION_REQUIRED:`); also returned by `agent ask` on a declined sign (no `--yes`), exit 12 |
825
+ | `ACTION_NOT_IMPLEMENTED` | Local executor does not implement this action type |
826
+ | `INVALID_INPUT` | Bad parameters, unknown chain, malformed NDJSON input, etc. |
827
+ | `NETWORK_ERROR` | RPC/fetch connectivity (includes many SDK `VaultError` network cases) |
828
+ | `TIMEOUT` | HTTP deadline or SSE frame-idle deadline exceeded (process exit 3, retryable) |
829
+ | `TRANSACTION_FAILED` | Build/broadcast/gas errors mapped from the SDK |
830
+ | `SIGNING_FAILED` | MPC/signing failed |
831
+ | `ACK_FAILED` | Transaction broadcast, but its immediate acknowledgement/report failed; hash is valid and must be inspected before retrying |
832
+ | `BROADCAST_COMMITTED` | At least one transaction broadcast, but the overall agent request may be incomplete; do not blindly retry |
833
+ | `AGENT_TURN_BLOCKED` | A fund-safety guardrail blocked the requested action (exit 10) |
834
+ | `AGENT_TURN_REFUSAL` | The model refused or requested clarification without completing the action (exit 11) |
835
+ | `AGENT_TURN_ERROR` | The typed turn ending reported a failure without a more specific stream error |
836
+ | `IDEMPOTENT_TURN_DUPLICATE` | The backend already accepted the same keyed turn; inspect the conversation for the original persisted result |
837
+ | `IDEMPOTENCY_KEY_REUSED` | The idempotency key was already used for a _different_ request body. This request did NOT run and nothing was persisted for it — retry with a fresh key (exit code 4, not 14) |
838
+ | `SESSION_NOT_INITIALIZED` | Internal session state error |
839
+ | `UNKNOWN_ERROR` | Unclassified failure (default for opaque SSE `error` events). Plain `AbortError` without “timeout” in the message maps here. |
747
840
 
748
841
  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.
749
842
 
750
843
  **Agent ask options:**
751
844
 
752
- - `--session <id>` - Continue an existing conversation
845
+ - `--session <id>` - Continue an existing conversation; a stale ID fails closed before the message is sent
753
846
  - `--backend-url <url>` - Agent backend URL (default: https://abe.vultisig.com)
754
847
  - `--password <password>` - Vault password (fallback only; prefer the keyring/`VAULT_PASSWORD` env — see **Password resolution** above)
755
848
  - `--verbose` - Show tool calls and debug info on stderr
756
849
  - `--json` - Output structured JSON
850
+ - `--yes` - Authorize unattended signing/broadcast
851
+ - `--allow-auto-submit` - Separately allow backend submission of signed Polymarket orders (requires `--yes` to sign)
852
+ - `--force` - Bypass the duplicate-broadcast guard
757
853
 
758
854
  #### Agent Chat (Interactive/Pipe Mode)
759
855
 
@@ -777,6 +873,7 @@ The vault password is resolved from the keyring/env chain (`vsig auth setup` or
777
873
  - `--password <password>` - Vault password (fallback only; prefer the keyring/`VAULT_PASSWORD` env)
778
874
  - `--password-ttl <ms>` - Password cache TTL (default: 5min, 24h for `--via-agent`)
779
875
  - `--session-id <id>` - Resume an existing session
876
+ - `--allow-auto-submit` - Allow backend submission of signed Polymarket orders after local confirmation
780
877
 
781
878
  #### Pipe Protocol (`--via-agent`)
782
879
 
@@ -792,19 +889,20 @@ The pipe interface uses NDJSON (one JSON object per line) on stdin/stdout. Desig
792
889
 
793
890
  **Output events** (emitted on stdout):
794
891
 
795
- | Type | Fields | When |
796
- | ------------- | ------------------------------------------- | -------------------------------------------------------------------------------------------------------- |
797
- | `ready` | `vault, addresses` | Session initialized, addresses for all chains |
798
- | `session` | `id` | Conversation ID for resuming later |
799
- | `history` | `messages[]` | Previous messages when resuming a session |
800
- | `text_delta` | `delta` | Streaming text chunk from the agent |
801
- | `tool_call` | `id, action, params?, status` | Action started (`running`) |
802
- | `tool_result` | `id, action, success, data?, error?, code?` | Action completed (`code` when `success` is false) |
803
- | `tx_status` | `tx_hash, chain, status, explorer_url?` | Transaction broadcast/confirmed/failed |
804
- | `assistant` | `content` | Full assistant response |
805
- | `suggestions` | `suggestions[]` | Suggested follow-up actions |
806
- | `error` | `message, code` | Error or control signal (`PASSWORD_REQUIRED`, `CONFIRMATION_REQUIRED: …`; always includes stable `code`) |
807
- | `done` | `{}` | Response cycle complete |
892
+ | Type | Fields | When |
893
+ | ------------- | ----------------------------------------------- | -------------------------------------------------------------------------------------------------------- |
894
+ | `ready` | `vault, addresses` | Session initialized, addresses for all chains |
895
+ | `session` | `id` | Conversation ID for resuming later |
896
+ | `history` | `messages[]` | Previous messages when resuming a session |
897
+ | `text_delta` | `delta` | Streaming text chunk from the agent |
898
+ | `tool_call` | `id, action, params?, status` | Action started (`running`) |
899
+ | `tool_result` | `id, action, success, data?, error?, code?` | Action completed (`code` when `success` is false) |
900
+ | `tx_status` | `tx_hash, chain, status, explorer_url?` | Transaction broadcast/confirmed/failed |
901
+ | `assistant` | `content` | Full assistant response |
902
+ | `suggestions` | `suggestions[]` | Suggested follow-up actions |
903
+ | `warning` | `warning: { code, message, count, eventTypes }` | Non-fatal protocol drift; an unrecognized SSE frame was ignored (`--verbose` only) |
904
+ | `error` | `message, code` | Error or control signal (`PASSWORD_REQUIRED`, `CONFIRMATION_REQUIRED: …`; always includes stable `code`) |
905
+ | `done` | `{}` | Response cycle complete |
808
906
 
809
907
  **Example session:**
810
908
 
@@ -855,10 +953,22 @@ VULTISIG_VAULT=MyWallet
855
953
  VAULT_PASSWORD=mypassword
856
954
 
857
955
  # Multiple vault passwords
858
- VAULT_PASSWORDS="Vault1:pass1 Vault2:pass2"
956
+ VAULT_PASSWORDS='{"Vault 1":"pass1","vault-id-2":"pass2"}'
859
957
 
860
958
  # Suppress spinners and info messages
861
959
  VULTISIG_SILENT=1
960
+
961
+ # Bound agent-backend connection/unary requests (default: 30000ms)
962
+ VULTISIG_HTTP_TIMEOUT_MS=30000
963
+
964
+ # Bound an established SSE stream that stops making PROGRESS (default: 180000ms).
965
+ # Measures time since the last real data frame. Keep-alive comments do NOT extend
966
+ # it — both backends heartbeat on a timer that runs regardless of whether the turn
967
+ # is advancing, so a clock they reset would bound only a dead connection, never a
968
+ # wedged backend. Sized above the backend's worst-case silent stretch (a model call
969
+ # is bounded at 90s; the swap builder is documented at 90s + 60s MCP), so a slow but
970
+ # healthy turn is never killed.
971
+ VULTISIG_SSE_IDLE_TIMEOUT_MS=180000
862
972
  ```
863
973
 
864
974
  ### Settings
@@ -1100,7 +1210,7 @@ VULTISIG_NO_UPDATE_CHECK=1
1100
1210
  VAULT_PASSWORD=mypassword
1101
1211
 
1102
1212
  # Multiple vault passwords
1103
- VAULT_PASSWORDS="Vault1:pass1 Vault2:pass2"
1213
+ VAULT_PASSWORDS='{"Vault 1":"pass1","vault-id-2":"pass2"}'
1104
1214
  ```
1105
1215
 
1106
1216
  ### Config Directory
@@ -1109,9 +1219,10 @@ Configuration is stored in `~/.vultisig/`:
1109
1219
 
1110
1220
  ```
1111
1221
  ~/.vultisig/
1112
- ├── config.json # User preferences
1113
- ├── vaults/ # Vault data
1114
- ├── cache/ # Version checks, etc.
1222
+ ├── config.json # User preferences and registered vaults
1223
+ ├── credentials.enc # Optional encrypted-file credential backend
1224
+ ├── vaults/ # Vault data
1225
+ ├── cache/ # Version checks, etc.
1115
1226
  └── address-book.json
1116
1227
  ```
1117
1228
 
@@ -1149,6 +1260,10 @@ Configuration is stored in `~/.vultisig/`:
1149
1260
  | 10 | agent ask: a fund-safety guardrail blocked the requested action |
1150
1261
  | 11 | agent ask: the model refused or asked a clarifying question (no action taken) |
1151
1262
  | 12 | Interactive confirmation/input required but the session is non-interactive — pass --yes/--confirm or the required flag |
1263
+ | 13 | agent ask: transaction broadcast but the overall request may be incomplete — inspect the hash, do NOT blindly retry |
1264
+ | 14 | agent ask: duplicate keyed turn rejected — inspect the conversation for the original result |
1265
+ | 15 | No active vault selected — create, import, or switch to one |
1266
+ | 16 | Stored state is unreadable — repair it or re-import the vault from a .vult backup |
1152
1267
 
1153
1268
  > These are generated from the `ExitCode` enum in `src/core/errors.ts` (the single source of
1154
1269
  > truth) and are covered by a doc-lint test that fails if this table drifts from the code. Run