shinjuku-shield 0.3.110 → 0.3.111

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/README.md CHANGED
@@ -6,19 +6,19 @@ answers privately, in a terminal chat or for your agent app. Its `mcp`
6
6
  command runs a local MCP server (stdio) for your agent. Your keys never
7
7
  leave your machine.
8
8
 
9
- This is version 0.3.110. The npm packages `shinjuku-shield` and
9
+ This is version 0.3.111. The npm packages `shinjuku-shield` and
10
10
  `shinjuku-shielded` hold the same files. The command is `shinjuku`.
11
11
 
12
12
  ## Set up
13
13
 
14
14
  ```sh
15
- npm install -g shinjuku@0.3.110
15
+ npm install -g shinjuku@0.3.111
16
16
  shinjuku setup
17
17
  ```
18
18
 
19
19
  Type the two lines as they are. `npx install shinjuku` does not work: npx
20
20
  reads `install` as the name of another npm package. To set up without
21
- installing, type `npx shinjuku@0.3.110 setup`.
21
+ installing, type `npx shinjuku@0.3.111 setup`.
22
22
 
23
23
  Setup makes the wallet on this machine, a private passphrase file, and the
24
24
  checked proof tools (about 191 MB), and writes `config.json`. It also writes
@@ -36,13 +36,13 @@ Every wallet command works after `shinjuku`, for example `shinjuku balance`,
36
36
  Claude Code:
37
37
 
38
38
  ```sh
39
- claude mcp add --scope user shinjuku -- npx -y --no-audit --no-fund shinjuku-shield@0.3.110 mcp
39
+ claude mcp add --scope user shinjuku -- npx -y --no-audit --no-fund shinjuku-shield@0.3.111 mcp
40
40
  ```
41
41
 
42
42
  Claude Desktop (`claude_desktop_config.json`) or Cursor (`~/.cursor/mcp.json`):
43
43
 
44
44
  ```json
45
- { "mcpServers": { "shinjuku": { "command": "npx", "args": ["-y", "--no-audit", "--no-fund", "shinjuku-shield@0.3.110", "mcp"] } } }
45
+ { "mcpServers": { "shinjuku": { "command": "npx", "args": ["-y", "--no-audit", "--no-fund", "shinjuku-shield@0.3.111", "mcp"] } } }
46
46
  ```
47
47
 
48
48
  On native Windows, if the host cannot find `npx`, use `"command": "cmd"` and
package/manifest.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "schema": "carbon-shielded-wallet/bundle-manifest/v1",
3
3
  "file": "shinjuku-wallet.mjs",
4
- "sha256": "f6ab1c4098bf84c9d5af4dbbcbf9aa077430dd347aa14912fbaf571741bd314a",
5
- "bytes": 8332382,
6
- "sourceCommit": "f296b3b0e23b608d4bac4428a0e5763ba8313414",
4
+ "sha256": "ca9504f8356a05d2a1fc36428f6443419e6943406d11e201d4fa2ff9d6c84814",
5
+ "bytes": 8333810,
6
+ "sourceCommit": "b7bf7b1d3617d779db6012f60c35e34f7e8363da",
7
7
  "sourceDirty": false,
8
8
  "installRecordMatchesLockfile": true,
9
9
  "releaseRule": "valid only when two fresh npm ci --ignore-scripts builds on different OSes give the same sha256; installRecordMatchesLockfile true means every entry npm installed equals its package-lock.json entry and every entry not marked optional, devOptional included, is installed: an optional entry (a package for another platform) may be absent",
@@ -1037,5 +1037,5 @@
1037
1037
  "version": "1.8.0"
1038
1038
  }
1039
1039
  ],
1040
- "publishedAt": "2026-10-27T14:09:40.228Z"
1040
+ "publishedAt": "2026-10-27T14:51:57.097Z"
1041
1041
  }
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "shinjuku-shield",
3
- "version": "0.3.110",
3
+ "version": "0.3.111",
4
4
  "mcpName": "io.github.ShinjukuStaition/shinjuku-mcp",
5
5
  "description": "Shinjuku Shielded, the private x402 facilitator on Solana: the self-custody wallet CLI and its MCP server. Shield, pay, and unshield USDC from any MCP agent, under caps you set.",
6
- "shinjukuRelease": "f6ab1c40",
7
- "shinjukuSha256": "f6ab1c4098bf84c9d5af4dbbcbf9aa077430dd347aa14912fbaf571741bd314a",
6
+ "shinjukuRelease": "ca9504f8",
7
+ "shinjukuSha256": "ca9504f8356a05d2a1fc36428f6443419e6943406d11e201d4fa2ff9d6c84814",
8
8
  "license": "SEE LICENSE IN LICENSE.txt",
9
9
  "author": "Shinjuku Shielded",
10
10
  "homepage": "https://github.com/ShinjukuStaition/shinjuku-shield#readme",
@@ -119461,10 +119461,11 @@ function refusalSay(a, tool, again, npmVersion) {
119461
119461
  if (a.refundArrived === true) return `The seller did not take it. ${yours} is back in your private balance. ${other}`;
119462
119462
  return `The seller did not take it; ${total2 ? `${total2} USDC` : "your money"} comes back by itself${when}. ${other}`;
119463
119463
  }
119464
+ const askAgain = typeof a.newRequestId === "string" && a.newRequestId ? `To ask again, use request_id ${a.newRequestId}; it pays again.` : "A new request_id pays again.";
119464
119465
  if (code2 === "p03_float_receipt_pruned" && a.conversationNotKept === true) {
119465
- return "Paid once, not again. The wallet keeps no copy of a private conversation, so the answer cannot come back. A new request_id pays again.";
119466
+ return `Paid once, not again: the wallet keeps no copy of a private conversation. ${askAgain}`;
119466
119467
  }
119467
- if (code2 === "p03_float_receipt_pruned") return "This request was paid more than 10 minutes ago. A new request_id pays again.";
119468
+ if (code2 === "p03_float_receipt_pruned") return `This request was paid more than 10 minutes ago. ${askAgain}`;
119468
119469
  if (/^p03_(float|resource_payment)_request_(id_conflict|data_mismatch)$/.test(code2)) {
119469
119470
  const fresh = typeof a.newRequestId === "string" && a.newRequestId ? `request_id ${a.newRequestId}` : "a new request_id";
119470
119471
  return `Nothing was paid: this request_id belongs to an earlier payment. For this request, ask again with ${fresh}.`;
@@ -120032,6 +120033,10 @@ function transferSay(a) {
120032
120033
  const sent = usdcText2(a.send?.amountAtomic);
120033
120034
  const kept2 = a.send?.keptPrivateAtomic && a.send.keptPrivateAtomic !== "0" ? usdcText2(a.send.keptPrivateAtomic) : null;
120034
120035
  const later = "If this chat ends first, it continues at your next request.";
120036
+ if (a.replayed === true) {
120037
+ const fresh = typeof a.newRequestId === "string" && a.newRequestId ? `request_id ${a.newRequestId}` : "a new request_id";
120038
+ return `This request_id belongs to an earlier transfer to ${to}; this call moved nothing. To transfer again, ask with ${fresh}.`;
120039
+ }
120035
120040
  switch (a.stage) {
120036
120041
  case "selling":
120037
120042
  return `Selling the token for ${usdc6}; then it goes privately to ${to}. ${later}`;
@@ -127543,7 +127548,9 @@ ${canonical5}`;
127543
127548
  const float = floatRefusal(error);
127544
127549
  if (float && float[0] === code2) {
127545
127550
  const [, detail, sharedNext] = float;
127546
- const floatNext = inference ? sharedNext.replace(/\bx402_pay\b/g, tool) : sharedNext;
127551
+ const prunedId = code2 === "p03_float_receipt_pruned" ? newRequestIdFor(requestId) : void 0;
127552
+ const sharedFloatNext = inference ? sharedNext.replace(/\bx402_pay\b/g, tool) : sharedNext;
127553
+ const floatNext = prunedId ? `${sharedFloatNext}: use the new request_id "${prunedId}"` : sharedFloatNext;
127547
127554
  let one;
127548
127555
  try {
127549
127556
  one = readBalance(paidStore ?? session.openStore());
@@ -127578,6 +127585,7 @@ ${canonical5}`;
127578
127585
  ...typeof error?.feeAtomic === "string" ? { feeAtomic: error.feeAtomic } : {},
127579
127586
  // conversationNotKept (floatPay.mjs finishedPruned): a private conversation, whose answer the wallet never kept.
127580
127587
  ...error?.conversationNotKept === true ? { conversationNotKept: true } : {},
127588
+ ...prunedId ? { newRequestId: prunedId } : {},
127581
127589
  // floatId: Shinjuku's id of this payment, which wallet_receipts shows with the refund's state and transaction (realSpeed3
127582
127590
  // force-live 5r, 2026-10-22: the answer gave no id, so nobody could look the refund up).
127583
127591
  // refundAtomic and refundEtaSeconds (Shinjuku's time) for a proven refund; an unproven send carries ambiguous: true.
@@ -131714,7 +131722,7 @@ ${canonical5}`;
131714
131722
  properties: {
131715
131723
  amount: { type: "string", description: 'USDC to shield, like "0.37" (up to 6 decimals). Pass this or amount_atomic.' },
131716
131724
  amount_atomic: { type: "string", pattern: "^[1-9][0-9]*$", description: "USDC to shield, in atomic units (1000000 = 1 USDC, 370000 = 0.37 USDC): any amount from 10000 (0.01 USDC) while the balance stays at most 5 USDC." },
131717
- request_id: { type: "string", pattern: "^[A-Za-z0-9._:-]{1,128}$", description: "Optional. Your stable name for this deposit, like shield-1; pass it again to read the status." }
131725
+ request_id: { type: "string", pattern: "^[A-Za-z0-9._:-]{1,128}$", description: "Optional. Leave it out for a new deposit (the answer names the id it made). Pass the request_id of an earlier answer only to read or continue that deposit." }
131718
131726
  },
131719
131727
  required: [],
131720
131728
  additionalProperties: false
@@ -131732,7 +131740,7 @@ ${canonical5}`;
131732
131740
  amount: { type: "string", description: 'USDC to send, like "2" or "0.37" (up to 6 decimals). Pass this or amount_atomic.' },
131733
131741
  amount_atomic: { type: "string", pattern: "^[1-9][0-9]*$", description: "USDC to send, in atomic units (1000000 = 1 USDC)." },
131734
131742
  to: { type: "string", description: "The receiving Solana address (base58), exactly as the user gave it. Leave it out to send to the owner's own wallet when the owner set exactly one (wallet_balance session.unshield.to); with none or several set, pass it." },
131735
- request_id: { type: "string", pattern: "^[A-Za-z0-9._:-]{1,128}$", description: "Optional. Your stable name for this send, like send-1; pass it again to read the status." },
131743
+ request_id: { type: "string", pattern: "^[A-Za-z0-9._:-]{1,128}$", description: "Optional. Leave it out for a new send (the answer names the id it made). Pass the request_id of an earlier answer only to read or continue that send." },
131736
131744
  parts: { type: "integer", minimum: 1, maximum: 8, description: "Optional, default 1: send the amount as this many smaller payments (a private send), of random sizes in whole 0.01 USDC: the sizes differ only when the amount allows it (0.02 USDC in 2 goes as 0.01 + 0.01; privateSend lists each amount). Each one carries at least 10000 atomic (0.01 USDC). Above 1 it needs max_delay_minutes 1-60 (smaller payments with no wait are refused: they would leave together). Each one is sent after a random wait of up to max_delay_minutes (the first one too), so the chain shows no single exit of this amount at this time; max_delay_minutes alone delays one exit. " + SEND_RUNS_ON + ` Late ones never go together. While some are unsent, x402_pay may spend only the rest of the balance. The plan is saved in the wallet before the first one, so the same request_id (with the same \`parts\` and max_delay_minutes) continues after a restart and never sends one twice; the answer's privateSend lists each one (state, amount, when it may go, transactions), sentAtomic, and remainingAtomic. The user confirms the whole send once. Each one is a full unshield, so it takes as long as one, and the facilitator pays the network fee of each. Privacy limit (tell the user): every payment goes to the SAME \`to\`, so anyone who adds up that address's incoming transfers sees the total; the smaller payments hide the time and the single amount of the exit, not the sum the address received. wallet_cancel with the same request_id stops the ones not sent yet (their value stays in the private balance); while one is being sent, that call waits for it to finish, up to about ${CALL_BUDGET_MS / 1e3} s.` },
131737
131745
  max_delay_minutes: { type: "integer", minimum: 0, maximum: 60, description: "Optional, default 0: each smaller payment, the first included, is sent after a random wait of up to this many minutes (a private send)." }
131738
131746
  },
@@ -131773,7 +131781,7 @@ ${canonical5}`;
131773
131781
  properties: {
131774
131782
  amount: { type: "string", description: 'USDC to shield, like "0.37" (up to 6 decimals). Pass this or amount_atomic, unless action is cancel.' },
131775
131783
  amount_atomic: { type: "string", pattern: "^[1-9][0-9]*$", description: "USDC to shield, in atomic units (1000000 = 1 USDC, 370000 = 0.37 USDC): any amount from 10000 (0.01 USDC) while the balance stays at most 5 USDC. Required unless action is cancel." },
131776
- request_id: { type: "string", pattern: "^[A-Za-z0-9._:-]{1,128}$", description: "Optional for a deposit, needed for cancel. Your stable name for this deposit, like from-phantom-1; pass it again (with the same amount) to read the status." },
131784
+ request_id: { type: "string", pattern: "^[A-Za-z0-9._:-]{1,128}$", description: "Optional for a deposit, needed for cancel. Leave it out for a new deposit (the answer names the id it made). Pass the request_id of an earlier answer (with the same amount) only to read, continue, or cancel that deposit." },
131777
131785
  action: { type: "string", enum: ["deposit", "cancel"], description: "deposit (the default): start or read this deposit. cancel: close this request's one-time address while nothing at all reached it (no amount needed)." }
131778
131786
  },
131779
131787
  required: [],
@@ -132674,7 +132682,10 @@ ${canonical5}`;
132674
132682
  } else if (sendStop === "split") {
132675
132683
  next = `${sentSome ? "Nothing more was sent" : "Nothing was sent yet"}: the private balance holds the next payment of this send, but the wallet must first get that money ready to send. It does that by itself, at each wallet request. Do not cancel the send: tell the user it is not stuck. In a few minutes, ${poll} again: it continues the send where it stopped (it never sends a payment twice)`;
132676
132684
  }
132677
- const inBox = plan.approval?.by === "elicitation";
132685
+ if (extra.replayed === true) {
132686
+ next = `This request_id belongs to an earlier transfer; this call moved nothing new. About that transfer: ${next.replace(/\.$/, "")}. To transfer again, call wallet_transfer_token with the new request_id "${extra.newRequestId}"`;
132687
+ }
132688
+ const inBox = plan.approval?.by === "elicitation" && extra.replayed !== true;
132678
132689
  return json3({
132679
132690
  ok: true,
132680
132691
  ...view2,
@@ -132796,7 +132807,9 @@ ${canonical5}`;
132796
132807
  const existing = readTransferToken(store, id);
132797
132808
  if (existing) {
132798
132809
  if (!sameTransfer(existing, request)) return transferReused(id);
132799
- return TRANSFER_RUNNING_STATES.has(existing.state) && existing.state !== "sold_waiting_room" || tidyOpen(existing) ? await answerSoon(id, { reused: true }, call) : transferAnswer(existing, { reused: true });
132810
+ if (TRANSFER_RUNNING_STATES.has(existing.state) && existing.state !== "sold_waiting_room" || tidyOpen(existing)) return await answerSoon(id, { reused: true }, call);
132811
+ const ended = ["done", "cancelled"].includes(existing.state) ? { replayed: true, newRequestId: `${id.slice(0, 31)}-${Date.now().toString(36)}` } : {};
132812
+ return transferAnswer(existing, { reused: true, ...ended });
132800
132813
  }
132801
132814
  } catch (error) {
132802
132815
  return transferRefusal(error, "transfer_plan_unreadable", "tell your owner");
@@ -133042,7 +133055,7 @@ ${canonical5}`;
133042
133055
  token_mint: { type: "string", description: "The base58 mint address of the token to sell, like DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263 (BONK). Never a symbol." },
133043
133056
  amount_atomic: { type: "string", pattern: "^[1-9][0-9]*$", description: 'How much of the token to sell, in its own base units: "1250000" = 1.25 of a 6-decimal token.' },
133044
133057
  to: { type: "string", description: "The receiving Solana address (base58), exactly as the user gave it. It receives USDC and must already be able to receive USDC." },
133045
- request_id: { type: "string", pattern: "^[A-Za-z0-9_-]{1,40}$", description: "Your stable name for this transfer, like move-bonk-1; wallet_transfer_token_status takes it." },
133058
+ request_id: { type: "string", pattern: "^[A-Za-z0-9_-]{1,40}$", description: "A new name for each new transfer, like move-bonk-k7q2 (an id an earlier transfer used only reads that transfer and moves nothing); wallet_transfer_token_status takes it." },
133046
133059
  parts: { type: "integer", minimum: 2, maximum: 8, description: "Optional, default 2: the USDC leaves as this many payments of random sizes in whole 0.01 USDC (each at least 0.01 USDC; the sizes differ only when the amount allows it: 0.02 USDC in 2 goes as 0.01 + 0.01)." },
133047
133060
  max_delay_minutes: { type: "integer", minimum: 1, maximum: 60, description: "Optional, default 30: each payment waits a random time up to this many minutes before it leaves." },
133048
133061
  max_slippage_bps: { type: "integer", minimum: 1, maximum: 300, description: "Optional, default 50 (0.5 %): the sell fails rather than give less than the quote minus this." },