shinjuku-shielded 0.3.174 → 0.3.175

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,13 +6,13 @@ 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.174. The npm packages `shinjuku-shield` and
9
+ This is version 0.3.175. 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
- npx -y shinjuku@0.3.174 setup
15
+ npx -y shinjuku@0.3.175 setup
16
16
  ```
17
17
 
18
18
  Setup offers to install the `shinjuku` command and, on Windows, adds npm's
@@ -35,13 +35,13 @@ Every wallet command works after `shinjuku`, for example `shinjuku balance`,
35
35
  Claude Code:
36
36
 
37
37
  ```sh
38
- claude mcp add --scope user shinjuku -- npx -y --no-audit --no-fund shinjuku-shield@0.3.174 mcp
38
+ claude mcp add --scope user shinjuku -- npx -y --no-audit --no-fund shinjuku-shield@0.3.175 mcp
39
39
  ```
40
40
 
41
41
  Claude Desktop (`claude_desktop_config.json`) or Cursor (`~/.cursor/mcp.json`):
42
42
 
43
43
  ```json
44
- { "mcpServers": { "shinjuku": { "command": "npx", "args": ["-y", "--no-audit", "--no-fund", "shinjuku-shield@0.3.174", "mcp"] } } }
44
+ { "mcpServers": { "shinjuku": { "command": "npx", "args": ["-y", "--no-audit", "--no-fund", "shinjuku-shield@0.3.175", "mcp"] } } }
45
45
  ```
46
46
 
47
47
  On native Windows, if the host cannot find `npx`, use `"command": "cmd"` and
@@ -54,7 +54,7 @@ Codex (the Codex app and Codex CLI read the same file, `~/.codex/config.toml`):
54
54
  ```toml
55
55
  [mcp_servers.shinjuku]
56
56
  command = "npx"
57
- args = ["-y", "--no-audit", "--no-fund", "shinjuku-shield@0.3.174", "mcp"]
57
+ args = ["-y", "--no-audit", "--no-fund", "shinjuku-shield@0.3.175", "mcp"]
58
58
  startup_timeout_sec = 120
59
59
  tool_timeout_sec = 300
60
60
  ```
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": "1018a5346c79bc828bd3200c8c6c7d741cac55e279d5b88a70f895f44cc105c6",
5
- "bytes": 9392332,
6
- "sourceCommit": "1629d7280f4cef53ec8b06e6f303e11c2c8ac3fe",
4
+ "sha256": "dd3bab15896a11e725fd452abbfd4fbad9b412640201141c884a77bd6c81d5dd",
5
+ "bytes": 9395751,
6
+ "sourceCommit": "9dc2ed8abb49b3cce01ddd09f43128cc0aa5c885",
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",
@@ -1038,5 +1038,5 @@
1038
1038
  "version": "1.8.0"
1039
1039
  }
1040
1040
  ],
1041
- "publishedAt": "2026-11-05T13:40:19.887Z"
1041
+ "publishedAt": "2026-11-05T23:21:41.970Z"
1042
1042
  }
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "shinjuku-shielded",
3
- "version": "0.3.174",
3
+ "version": "0.3.175",
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": "1018a534",
7
- "shinjukuSha256": "1018a5346c79bc828bd3200c8c6c7d741cac55e279d5b88a70f895f44cc105c6",
6
+ "shinjukuRelease": "dd3bab15",
7
+ "shinjukuSha256": "dd3bab15896a11e725fd452abbfd4fbad9b412640201141c884a77bd6c81d5dd",
8
8
  "license": "SEE LICENSE IN LICENSE.txt",
9
9
  "author": "Shinjuku Shielded",
10
10
  "homepage": "https://github.com/ShinjukuStaition/shinjuku-shield#readme",
@@ -54474,7 +54474,7 @@ function hostStartFolder(target, entry) {
54474
54474
  }
54475
54475
  function noStartFolderWords(target, entry) {
54476
54476
  if (hasPrefix(entry) || hostStartFolder(target, entry) !== void 0) return "";
54477
- return ` Your wallet folder path has a character that Windows reads as code (% & | < > ^ ! or "), so ${HOST_NAMES[target]} starts the wallet in a folder of its own: a settings file there (.npmrc) can stop the wallet. If the wallet does not start there, set up the wallet again in a folder without these characters.`;
54477
+ return ` Your wallet folder path has a character that Windows reads as code (% & | < > ^ ! " $ or \`), so ${HOST_NAMES[target]} starts the wallet in a folder of its own: a settings file there (.npmrc) can stop the wallet. If the wallet does not start there, set up the wallet again in a folder without these characters.`;
54478
54478
  }
54479
54479
  function cwdFolder(cwd) {
54480
54480
  if (typeof cwd !== "string" || !cwd) return void 0;
@@ -54492,12 +54492,12 @@ function installPackageWord(npmVersion) {
54492
54492
  const version3 = knownNpmVersion(npmVersion);
54493
54493
  return version3 ? `${COMMAND_PACKAGE}@${version3}` : COMMAND_PACKAGE;
54494
54494
  }
54495
- function oneTimeCommandLine(words2, npmVersion) {
54496
- return [npxCommandPrefix(npmVersion, { recovery: RECOVERY_WORDS.test(String(words2 ?? "")) }), words2].join(" ");
54495
+ function oneTimeCommandLine(words2, npmVersion, { npmArgs = [] } = {}) {
54496
+ return [npxCommandPrefix(npmVersion, { recovery: RECOVERY_WORDS.test(String(words2 ?? "")), npmArgs }), words2].join(" ");
54497
54497
  }
54498
- function npxCommandPrefix(npmVersion, { recovery = false } = {}) {
54498
+ function npxCommandPrefix(npmVersion, { recovery = false, npmArgs = [] } = {}) {
54499
54499
  const cache = knownNpmVersion(npmVersion) && !recovery ? npxCacheFlag() : void 0;
54500
- return ["npx", ...cache ? [cache] : [], "-y", ...NPX_INSTALL_FLAGS, npxPackageWord(npmVersion)].join(" ");
54500
+ return ["npx", ...cache ? [cache] : [], "-y", ...NPX_INSTALL_FLAGS, ...npmArgs, npxPackageWord(npmVersion)].join(" ");
54501
54501
  }
54502
54502
  function npxLineFlags(text3, ownVersion) {
54503
54503
  const own = knownNpmVersion(ownVersion);
@@ -54539,7 +54539,7 @@ function mcpServerEntry({ home, platform = process.platform, npmVersion, path, t
54539
54539
  }
54540
54540
  function npmPrefixArgs(home, platform = process.platform) {
54541
54541
  const folder = resolve6(home);
54542
- return platform === "win32" && CMD_ARG_CODE.test(folder) ? [] : [`--prefix=${folder}`];
54542
+ return platform === "win32" && WIN_ARG_CODE.test(folder) ? [] : [`--prefix=${folder}`];
54543
54543
  }
54544
54544
  function homeTorOn(home) {
54545
54545
  try {
@@ -55812,7 +55812,7 @@ function changeFileOnce(file, change, { now, platform, restrict, codec = JSON_CO
55812
55812
  }
55813
55813
  return { changed: true, ...backup ? { backup } : {} };
55814
55814
  }
55815
- var import_web396, CONFIG_SCHEMA, CONFIG_FILE, NPM_PACKAGE_NAMES, COMMAND_PACKAGE, NPX_PACKAGE, NPX_INSTALL_FLAGS, MCP_SERVER_NAME, OLD_MCP_SERVER_NAMES, P03_BINDING, PRODUCTION, RETIRED_POOLS, NO_LIMIT_ATOMIC, DEFAULT_CAPS, OLD_DEFAULT_SETUP_CAPS, OLD_DEFAULT_WALLET_BUDGETS, capText, POSITIVE_ATOMIC, HOME_FOLDER, OLDER_HOME_FOLDER, defaultHomeWords, configPath, homeWalletPath, invalid, isPubkey, MAX_OWN_ADDRESSES, isOwnAddressText, salvagePath, clearConfigSalvage, HOST_TARGETS, HOST_NAMES, hostServersKey, CWD_HOSTS, hasPrefix, CODEX_STARTUP_TIMEOUT_SEC, CODEX_TOOL_TIMEOUT_SEC, PINNED_PACKAGE, LAUNCHER_PACKAGE, NPM_VERSION, knownNpmVersion, RECOVERY_WORDS, NPX_WALLET_LINE, RELEASES_URL, npmStartEnvironment, CMD_ARG_CODE, NPM_START_KEYS, defaultWalletHome, CMD_UNSAFE, programOf, claudeLineDiffers, WINDOWS_SHELL_CODE, CLAUDE_DESKTOP_FILE, claudeDataDirsCache, CONTINUE_HEADER, tomlString, tomlKey, codexEntryOf, HERMES_TRUST, HERMES_TOOL_TIMEOUT_SEC, hermesCwd, HERMES_OWN_KEYS, HERMES_RESERVED_PROFILES, yamlString, hermesEntryOf, isPlainObject, sameJson, claudeCodeEntry, READ_ONLY_TOOL_NAMES, claudeCodeAllowRules, claudeCodeSettingsFile, UNREADABLE_JSON, JSON_CODEC, indentAt, TEXT_CODEC, RACED;
55815
+ var import_web396, CONFIG_SCHEMA, CONFIG_FILE, NPM_PACKAGE_NAMES, COMMAND_PACKAGE, NPX_PACKAGE, NPX_INSTALL_FLAGS, MCP_SERVER_NAME, OLD_MCP_SERVER_NAMES, P03_BINDING, PRODUCTION, RETIRED_POOLS, NO_LIMIT_ATOMIC, DEFAULT_CAPS, OLD_DEFAULT_SETUP_CAPS, OLD_DEFAULT_WALLET_BUDGETS, capText, POSITIVE_ATOMIC, HOME_FOLDER, OLDER_HOME_FOLDER, defaultHomeWords, configPath, homeWalletPath, invalid, isPubkey, MAX_OWN_ADDRESSES, isOwnAddressText, salvagePath, clearConfigSalvage, HOST_TARGETS, HOST_NAMES, hostServersKey, CWD_HOSTS, hasPrefix, CODEX_STARTUP_TIMEOUT_SEC, CODEX_TOOL_TIMEOUT_SEC, PINNED_PACKAGE, LAUNCHER_PACKAGE, NPM_VERSION, knownNpmVersion, RECOVERY_WORDS, NPX_WALLET_LINE, RELEASES_URL, npmStartEnvironment, WIN_ARG_CODE, NPM_START_KEYS, defaultWalletHome, CMD_UNSAFE, programOf, claudeLineDiffers, WINDOWS_SHELL_CODE, CLAUDE_DESKTOP_FILE, claudeDataDirsCache, CONTINUE_HEADER, tomlString, tomlKey, codexEntryOf, HERMES_TRUST, HERMES_TOOL_TIMEOUT_SEC, hermesCwd, HERMES_OWN_KEYS, HERMES_RESERVED_PROFILES, yamlString, hermesEntryOf, isPlainObject, sameJson, claudeCodeEntry, READ_ONLY_TOOL_NAMES, claudeCodeAllowRules, claudeCodeSettingsFile, UNREADABLE_JSON, JSON_CODEC, indentAt, TEXT_CODEC, RACED;
55816
55816
  var init_config = __esm({
55817
55817
  "wallet/src/setup/config.mjs"() {
55818
55818
  "use strict";
@@ -55901,7 +55901,7 @@ var init_config = __esm({
55901
55901
  NPX_WALLET_LINE = /npx (?:--(?:prefer-)?offline )?-y ((?:--no-audit --no-fund )?(?:shinjuku-shield|shinjuku)@(\d+\.\d+\.\d+))((?: (?:setup|restore))(?=\s|$|[^\w-]))?/g;
55902
55902
  RELEASES_URL = "https://github.com/ShinjukuStaition/shinjuku-shield#releases";
55903
55903
  npmStartEnvironment = (tor) => tor ? { npm_config_offline: "true" } : { npm_config_prefer_offline: "true" };
55904
- CMD_ARG_CODE = /[%&|<>^!"\r\n]/;
55904
+ WIN_ARG_CODE = /[%&|<>^!"$`\r\n]/;
55905
55905
  NPM_START_KEYS = ["npm_config_offline", "npm_config_prefer_offline"];
55906
55906
  defaultWalletHome = (home = homedir()) => setupHome({}, home);
55907
55907
  CMD_UNSAFE = /[%"\r\n]/;
@@ -135122,7 +135122,8 @@ ${canonical5}`;
135122
135122
  return said;
135123
135123
  }
135124
135124
  if (typeof parsed2.next !== "string") return said;
135125
- parsed2.next = `${parsed2.next.replace(/[.\s]+$/, "")}. ${nextNote}`;
135125
+ const note = typeof nextNote === "function" ? nextNote(parsed2) : nextNote;
135126
+ parsed2.next = `${parsed2.next.replace(/[.\s]+$/, "")}. ${note}`;
135126
135127
  const out = JSON.stringify(parsed2, null, /^\s*\{\s*\n/.test(text3) ? 2 : void 0);
135127
135128
  return { ...said, text: out, ...blocks ? { content: [{ ...blocks[0], text: out }, ...blocks.slice(1)] } : {} };
135128
135129
  }
@@ -138013,7 +138014,7 @@ ${canonical5}`;
138013
138014
  const sendStoppedFailure = (job) => ({
138014
138015
  code: SEND_STOPPED,
138015
138016
  detail: `the app stopped the call before the send of ${usdcText3(job.amountAtomic)} USDC was signed, so the wallet stopped it; nothing was sent`,
138016
- next: "Nothing was sent. Tell the user the send was stopped and nothing left the private balance. Send it only if the user asks again: then call wallet_unshield with a NEW request_id (it asks the user again)",
138017
+ next: "Nothing was sent. Tell the user the send was stopped and nothing left the private balance. Send it only if the user asks again: then call wallet_unshield with a NEW request_id (it asks the user again, except for an address the user saved, which sends at once)",
138017
138018
  say: `Nothing was sent: the send of ${usdcText3(job.amountAtomic)} USDC was stopped before it was signed.`
138018
138019
  });
138019
138020
  function jobFailure(kind, job, error, linkHeld = /* @__PURE__ */ new Map()) {
@@ -138797,16 +138798,30 @@ ${canonical5}`;
138797
138798
  return { ok: false, why: error?.timedOut === true ? `no answer in ${Math.round(elicitTimeoutMs / 1e3)} s` : "this app did not answer the confirmation" };
138798
138799
  }
138799
138800
  }
138800
- function intentDoneAtMs(kind) {
138801
+ function intentDoneAtMs(kind, stopped = {}) {
138802
+ const released = () => {
138803
+ stopped.seen = true;
138804
+ return stopped.release === true ? 0 : void 0;
138805
+ };
138801
138806
  return (requestId, store) => {
138802
138807
  const job = jobs.get(`${kind}:${toRecordId(requestId)}`);
138803
138808
  if (job?.state === "done") return job.doneAtMs;
138809
+ if (kind === "unshield" && job?.state === "failed" && job.error?.code === SEND_STOPPED) return released();
138804
138810
  if (job?.state === "failed") return /NEW request_id|Do not retry this request/.test(String(job.error?.next ?? "")) ? 0 : void 0;
138805
138811
  if (job) return void 0;
138806
138812
  const names = kind === "unshield" ? [`self-pay-${moneyId(requestId)}`, privateSendPlanName(moneyId(requestId))] : [KINDS[kind].walName(requestId)];
138807
138813
  for (const name of names) {
138808
138814
  const record = store.readWal(name);
138809
138815
  if (!record) continue;
138816
+ if (kind === "unshield" && name === names[0] && record.schema === SELF_PAY_WAL_SCHEMA && record.state === "started" && record.refusedCode === SEND_STOPPED) {
138817
+ let signed = true;
138818
+ try {
138819
+ signed = selfPayMayHaveSigned(store, moneyId(requestId));
138820
+ } catch {
138821
+ signed = true;
138822
+ }
138823
+ return signed ? void 0 : released();
138824
+ }
138810
138825
  const finished = kind !== "unshield" ? ["published", "foreign_funded", "reclaimed"].includes(record.state) : record.schema === PRIVATE_SEND_WAL_SCHEMA ? record.state === "done" || record.state === "cancelled" && privateSendStatus(store, record).done : ["exited", "released", "abandoned"].includes(record.state);
138811
138826
  if (!finished) return void 0;
138812
138827
  const at = Date.parse(String(record.doneAt ?? "")) || sentAtMs(record);
@@ -138851,13 +138866,30 @@ ${canonical5}`;
138851
138866
  }
138852
138867
  return void 0;
138853
138868
  }
138869
+ const CLAIM_LEASE_WAIT_MS = 8e3;
138870
+ async function claimUnderLease(task) {
138871
+ const until = Date.now() + CLAIM_LEASE_WAIT_MS;
138872
+ for (; ; ) {
138873
+ try {
138874
+ return await underLease(task);
138875
+ } catch (error) {
138876
+ if (!/wallet_process_locked|wallet_locked/.test(String(error?.message ?? error)) || Date.now() >= until) throw error;
138877
+ await new Promise((resolve43) => {
138878
+ const timer = setTimeout(resolve43, 250);
138879
+ timer.unref?.();
138880
+ });
138881
+ }
138882
+ }
138883
+ }
138854
138884
  async function beginWithoutId(kind, args, options) {
138855
138885
  const spec = KINDS[kind];
138856
138886
  const probe = argumentsOf(kind, { ...args, request_id: "intent-probe" }, { probe: true });
138857
138887
  if (probe.refusal) return refuse12(...probe.refusal);
138858
138888
  let claimed;
138889
+ const stopped = {};
138859
138890
  try {
138860
- claimed = intents.claim(kind, probe, INTENT_WINDOW_MS, intentDoneAtMs(kind));
138891
+ const found = intents.find(kind, probe, INTENT_WINDOW_MS, intentDoneAtMs(kind, stopped));
138892
+ if (found && !stopped.seen) claimed = { ...found, created: false };
138861
138893
  } catch (error) {
138862
138894
  return refuse12(
138863
138895
  "mcp_wallet_unreadable",
@@ -138865,13 +138897,21 @@ ${canonical5}`;
138865
138897
  `call ${spec.tool} again, or pass your own request_id`
138866
138898
  );
138867
138899
  }
138900
+ if (!claimed) {
138901
+ try {
138902
+ claimed = await claimUnderLease(() => intents.claim(kind, probe, INTENT_WINDOW_MS, intentDoneAtMs(kind, { release: true })));
138903
+ } catch (error) {
138904
+ return /^(?:wallet_locked|wallet_process_locked)$/.test(errorCode2(error, "wallet_locked")) ? refuse12(
138905
+ "wallet_locked",
138906
+ "another program is using this wallet right now, so this call did not start anything; another app may be running the same request",
138907
+ `This call did not start a ${spec.noun}. Do not tell the user whether money moved: first call ${spec.tool === "wallet_unshield" ? "wallet_cancel with no request_id, or wallet_receipts" : "wallet_receipts"}, or call ${spec.tool} again with the same arguments in about a minute (it reads the same request and never moves money twice). If it repeats, tell the user another program (for example a Shinjuku terminal window) is using the wallet`,
138908
+ { done: false, say: "The wallet is busy with another program right now, so this request did not start here. I will check and try again.", ...lockedFields() }
138909
+ ) : refuse12("mcp_wallet_unreadable", describeError(error, { fallback: "wallet_store_unreadable" }), `call ${spec.tool} again, or pass your own request_id`);
138910
+ }
138911
+ }
138868
138912
  if (claimed.created) log(`MCP_INTENT_NEW kind=${kind} requestId=${claimed.requestId}`);
138869
138913
  const answer = await begin(kind, { ...args, request_id: claimed.requestId }, options);
138870
- return withAutoId(
138871
- answer,
138872
- spec.tool,
138873
- "The same call without request_id within 10 minutes after it ends reads this same request; if the user wants to do it again later, ask after 10 minutes or give a new request_id."
138874
- );
138914
+ return withAutoId(answer, spec.tool, (parsed2) => kind === "unshield" && parsed2.error === SEND_STOPPED ? "The same call without request_id reads this stopped send again; to send it, the user must ask again and you pass a NEW request_id." : "The same call without request_id within 10 minutes after it ends reads this same request; if the user wants to do it again later, ask after 10 minutes or give a new request_id.");
138875
138915
  }
138876
138916
  function stageChanged(job) {
138877
138917
  if (!job.changed) {
@@ -140556,7 +140596,7 @@ ${canonical5}`;
140556
140596
  name: "wallet_unshield",
140557
140597
  // UX130-13: a host that defers tools may show only the title: it says where a send goes by default.
140558
140598
  title: "Send USDC privately",
140559
- description: `Use this when the user says "send 2 USDC to <address>" or "withdraw to my wallet" (for "my wallet" leave \`to\` out: it uses the address saved at setup, or says how to set one; never ask the user first): it sends amount (USDC, like "2"; or amount_atomic) from the PRIVATE balance as plain USDC to \`to\`, a public Solana address. BEFORE you call it to send all of the balance when wallet_balance showed balanceParts above 1, write to the user that the wallet first gets the balance ready to send (do not tell the user how the wallet stores the balance; some apps show nothing until this call ends). Privately: the chain shows the pool paying the address, not which wallet paid. The wallet's own key (ownAddress in wallet_balance) is this wallet's public address: it holds the public USDC, wallet_shield deposits from it, and wallet_transfer_token sells from it. So it is refused as \`to\`: an exit there would show on chain that the private balance belongs to this wallet. This holds whether or not that key ever deposited: wallet_add_funds deposits come from one-time addresses, not from this key, so never tell the user the deposits came from it. Shinjuku pays every network fee; no SOL is needed. The user's saved own addresses (setup or this app's wallet entry) send at once. Any other address is sent only after the user confirms it in this app (a confirmation dialog); a client that cannot ask is refused (mcp_unshield_to_not_allowed) and a decline sends nothing (mcp_unshield_declined). \`to\` must already be able to receive USDC: an address that cannot is refused before any dialog (mcp_unshield_to_invalid). It runs in steps and may wait for the pool's next anchor: the call answers within about 20 s with stage (confirming, queued, refreshing, self-pay = preparing, exit = sending, done; joining = the wallet first gets the balance ready to send, then sends; each call goes on with it (joinSteps: its steps, one after another; doneInMinutesLow to doneInMinutesHigh: the time to say), ok: true); call wallet_unshield again with the SAME request_id, amount, and to for the status (each call answers within about 20 s, at once when the send ends; a confirmation dialog the user answers may take longer). When the wallet must first get the balance ready to send, the FIRST call answers at once with stage joining and joinTellFirst: true; write its say to the user, then call again with the same request_id at once. One exception: for a send whose balance must first be made ready, when this app sends a progress token with the call, that next call gets the balance ready and then sends, with a progress line every ${JOIN_STEP_MS / 1e3} s ("getting your balance ready to send"; some apps show none of them, so the line is said before the call, as above). It answers when the send is done, or after about ${joinAnswerSeconds(JOIN_CALL_MAX_MS)} s with how far it got (joinsDone; sendState sending: the USDC is leaving, never say nothing moved); then call again with the same request_id. Without a progress token it answers within about 20 s with stage joining: call again until it sends. The same request_id never sends twice, also after a restart. request_id is optional: without it the answer names the id it made, and the same call again within 10 minutes after it ends reads that same send. An optional limit the user sets (the unshield limit) may bound each call and what this wallet sends in any 24 hours, from every app that runs it (mcp_unshield_cap_exceeded: tell the user). Other refusals: mcp_argument_invalid, mcp_request_id_reused, wallet_locked, mcp_tool_disabled, mcp_unshield_failed, and the unshield's own p03_* codes. A call the app stopped, or one that failed, may still send: call wallet_cancel before you tell the user anything about that send; never say nothing was sent without it. A send is one payment to \`to\`. ${HOST_BLOCKED_LINE}`,
140599
+ description: `Use this when the user says "send 2 USDC to <address>" or "withdraw to my wallet" (for "my wallet" leave \`to\` out: it uses the address saved at setup, or says how to set one; never ask the user first): it sends amount (USDC, like "2"; or amount_atomic) from the PRIVATE balance as plain USDC to \`to\`, a public Solana address. BEFORE you call it to send all of the balance when wallet_balance showed balanceParts above 1, write to the user that the wallet first gets the balance ready to send (do not tell the user how the wallet stores the balance; some apps show nothing until this call ends). Privately: the chain shows the pool paying the address, not which wallet paid. The wallet's own key (ownAddress in wallet_balance) is this wallet's public address: it holds the public USDC, wallet_shield deposits from it, and wallet_transfer_token sells from it. So it is refused as \`to\`: an exit there would show on chain that the private balance belongs to this wallet. This holds whether or not that key ever deposited: wallet_add_funds deposits come from one-time addresses, not from this key, so never tell the user the deposits came from it. Shinjuku pays every network fee; no SOL is needed. The user's saved own addresses (setup or this app's wallet entry) send at once. Any other address is sent only after the user confirms it in a dialog of this app; a client that cannot ask is refused (mcp_unshield_to_not_allowed) and a decline sends nothing (mcp_unshield_declined). \`to\` must already be able to receive USDC: an address that cannot is refused before any dialog (mcp_unshield_to_invalid). It runs in steps and may wait for the pool's next anchor: the call answers within about 20 s with stage (confirming, queued, refreshing, self-pay = preparing, exit = sending, done; joining = the wallet first gets the balance ready to send, then sends; each call goes on with it (joinSteps: its steps, one after another; doneInMinutesLow to doneInMinutesHigh: the time to say), ok: true); call wallet_unshield again with the SAME request_id, amount, and to for the status (each call answers within about 20 s, at once when the send ends; a dialog may take longer). When the wallet must first get the balance ready to send, the FIRST call answers at once with stage joining and joinTellFirst: true; write its say to the user, then call again with the same request_id at once. One exception: for a send whose balance must first be made ready, when this app sends a progress token with the call, that next call gets the balance ready and then sends, with a progress line every ${JOIN_STEP_MS / 1e3} s ("getting your balance ready to send"; some apps show none of them, so the line is said before the call, as above). It answers when the send is done, or after about ${joinAnswerSeconds(JOIN_CALL_MAX_MS)} s with how far it got (joinsDone; sendState sending: the USDC is leaving, never say nothing moved); then call again with the same request_id. Without a progress token it answers within about 20 s with stage joining: call again until it sends. The same request_id never sends twice, also after a restart. request_id is optional: without it the answer names the id it made, and the same call again within 10 minutes after it ends reads that same send. An optional limit the user sets (the unshield limit) may bound each call and what this wallet sends in any 24 hours, from every app that runs it (mcp_unshield_cap_exceeded: tell the user). Other refusals: mcp_argument_invalid, mcp_request_id_reused, wallet_locked, mcp_tool_disabled, mcp_unshield_failed, and the unshield's own p03_* codes. A call the app stopped or calls rejected or interrupted, or one that failed, may still send: call wallet_cancel (no request_id) before you tell the user anything about that send; never say nothing was sent without it. A send is one payment to \`to\`. ${HOST_BLOCKED_LINE}`,
140560
140600
  inputSchema: {
140561
140601
  type: "object",
140562
140602
  properties: {
@@ -145702,7 +145742,7 @@ var init_stdioServer = __esm({
145702
145742
  SUPPORTED_PROTOCOL_VERSIONS = Object.freeze(["2025-06-18", "2025-03-26", "2024-11-05"]);
145703
145743
  MAX_LINE_BYTES = 4 * 1024 * 1024;
145704
145744
  POSITIVE_ATOMIC7 = /^[1-9][0-9]{0,30}$/;
145705
- INSTRUCTIONS_BASE = `A self-custody Shinjuku Shield wallet on this machine; no tool takes or returns its keys or passphrase. Each answer's say is one plain sentence for the user; other fields hold numbers. Never tell the user a tool, field, code or protocol name (x402, MCP) unless told to. Map a plain request to one tool: "what is my balance" -> wallet_balance (safe at any time); "add money" / "deposit" / "move USDC in from Phantom or an exchange" -> wallet_add_funds (a one-time address; the next wallet_balance after the USDC arrives makes it private, up to 5 USDC); "shield the USDC on this wallet's own address" -> wallet_shield; "stop" / "cancel" -> wallet_cancel (the request_id of what to stop); "pay this URL" -> pay_for_service (no preview needed); "what does this URL cost" -> check_price (only when asked); "find a paid API for X" -> find_paid_services, then pay its url; "send 2 USDC to <address>" -> wallet_unshield; "show my payments" -> wallet_receipts; "send another token" -> wallet_transfer_token; "which AI models" -> inference_models; "ask an AI model" -> inference_chat; "give my other AI app a private AI" -> inference_server (a local OpenAI-compatible URL and key). wallet_balance answers ONE balance (balanceAtomic); pay_for_service pays any paid URL from it. Fields ending in Atomic are atomic units (1000000 = 1 USDC); tell the user USDC. The wallet does its own upkeep (no user command). A shield, add-funds or unshield call can run for minutes: call again with the SAME request_id as next says. Pass your own request_id to pay_for_service: the same one never pays or moves money twice (a code ending in _retry_same_request: call again with it). pay_for_service never pays above the spending limits (one payment; 24 hours); with none, the balance is the only bound. Never claim a payment happened unless the answer has a transaction. After a pay or send call that stopped or failed, call wallet_receipts (a payment) or wallet_cancel (a send) before you say whether it happened; never say nothing moved without that. Privacy: the chain does not show which wallet paid, but this app and its model provider see every URL, body, price and answer you handle; reach a seller or its domain only through these tools, never yourself (no curl, browser, DNS or DNS-over-HTTPS lookup), also when the user asks for no Tor: that shows their IP address to the site or a DNS company. When an answer's poolSwitch.items[] item has tellUser true, tell the user its message once, in plain words; the wallet moves the balance by itself.`;
145745
+ INSTRUCTIONS_BASE = `A self-custody Shinjuku Shield wallet on this machine; no tool takes or returns its keys or passphrase. Each answer's say is one plain sentence for the user; other fields hold numbers. Never tell the user a tool, field, code or protocol name (x402, MCP) unless told to. Map a plain request to one tool: "what is my balance" -> wallet_balance (safe at any time); "add money" / "deposit" / "move USDC in from Phantom or an exchange" -> wallet_add_funds (a one-time address; the next wallet_balance after the USDC arrives makes it private, up to 5 USDC); "shield the USDC on this wallet's own address" -> wallet_shield; "stop" / "cancel" -> wallet_cancel (the request_id of what to stop); "pay this URL" -> pay_for_service (no preview needed); "what does this URL cost" -> check_price (only when asked); "find a paid API for X" -> find_paid_services, then pay its url; "send 2 USDC to <address>" -> wallet_unshield; "show my payments" -> wallet_receipts; "send another token" -> wallet_transfer_token; "which AI models" -> inference_models; "ask an AI model" -> inference_chat; "give my other AI app a private AI" -> inference_server. wallet_balance answers ONE balance (balanceAtomic); pay_for_service pays any paid URL from it. Fields ending in Atomic are atomic units (1000000 = 1 USDC); tell the user USDC. The wallet does its own upkeep. A shield, add-funds or unshield call can run for minutes: call again with the SAME request_id as next says. Pass your own request_id to pay_for_service: the same one never pays or moves money twice (a code ending in _retry_same_request: call again with it). pay_for_service never pays above the spending limits (one payment; 24 hours); with none, the balance is the only bound. Never claim a payment happened unless the answer has a transaction. After a pay or send call that stopped or failed (also one this app calls rejected or interrupted), call wallet_receipts (a payment) or wallet_cancel (a send; no request_id) before you say whether it happened; never say nothing moved without that. Privacy: the chain does not show which wallet paid, but this app and its model provider see every URL, body, price and answer you handle; reach a seller or its domain only through these tools, never yourself (no curl, browser, DNS or DNS-over-HTTPS lookup), also when the user asks for no Tor: that shows their IP address to the site or a DNS company. When an answer's poolSwitch.items[] item has tellUser true, tell the user its message once, in plain words; the wallet moves the balance by itself.`;
145706
145746
  SELL_LINE = "To sell or swap a token the wallet holds: wallet_transfer_token.";
145707
145747
  OWN_ADDRESS_LINE = '"Send to my (main) wallet": wallet_unshield with no to uses the address saved at setup; never ask for it.';
145708
145748
  INSTRUCTIONS = `${INSTRUCTIONS_BASE} ${SELL_LINE} ${OWN_ADDRESS_LINE} ${HOST_BLOCKED_LINE}`;
@@ -162197,7 +162237,10 @@ async function runSetupCommand(values, deps) {
162197
162237
  readsBack ? "Setup keeps the restored wallet on this machine (your keys never leave it), downloads and checks the setup files, and writes config.json." : "Setup makes the wallet on this machine (your keys never leave it), downloads and checks the setup files, and writes config.json.",
162198
162238
  // 0.3.97 T4, live proof 2026-10-24: the command on its own line.
162199
162239
  "An agent app then starts it with this command:",
162200
- ` ${oneTimeCommandLine("mcp", npmVersion)}`,
162240
+ // Row 25 rerun on 0.3.174 (check C): the line a user may copy into an app setup cannot add has the app entry's
162241
+ // --prefix (npm reads no .npmrc of the folder the app starts in); a Windows home with a cmd sign, $ or ` gets none, as
162242
+ // the entry. Quoted for the user's shell (hostShellWord: single quotes on POSIX; Codex round 1 on 1b3aaa99 item 2).
162243
+ ` ${oneTimeCommandLine("mcp", npmVersion, { npmArgs: npmPrefixArgs(home, deps.platform ?? process.platform).map((word2) => hostShellWord(word2, deps.platform ?? process.platform)) })}`,
162201
162244
  ""
162202
162245
  ],
162203
162246
  // The status line too, when a terminal setup started this run and printed it (AB234's live run 2026-11-04 showed