shinjuku-shield 0.3.112 → 0.3.113

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.112. The npm packages `shinjuku-shield` and
9
+ This is version 0.3.113. 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.112
15
+ npm install -g shinjuku@0.3.113
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.112 setup`.
21
+ installing, type `npx shinjuku@0.3.113 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.112 mcp
39
+ claude mcp add --scope user shinjuku -- npx -y --no-audit --no-fund shinjuku-shield@0.3.113 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.112", "mcp"] } } }
45
+ { "mcpServers": { "shinjuku": { "command": "npx", "args": ["-y", "--no-audit", "--no-fund", "shinjuku-shield@0.3.113", "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": "1406c0d44399772d43e33dc6b4cfb7428f9f24215e13389ac46f5dd6dc28d224",
5
- "bytes": 8331849,
6
- "sourceCommit": "c46e085c7ef73488d9a62c58b19f13891a4724b5",
4
+ "sha256": "bfaedd12d120b4b5421cdf6c9c06b07b7e338fd6d4e74b7225f2a0d3aa150cf4",
5
+ "bytes": 8339853,
6
+ "sourceCommit": "0c37c0dea055d3c28bab9939cd5527fc290668f8",
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-27T15:32:15.515Z"
1040
+ "publishedAt": "2026-10-27T16:17:32.089Z"
1041
1041
  }
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "shinjuku-shield",
3
- "version": "0.3.112",
3
+ "version": "0.3.113",
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": "1406c0d4",
7
- "shinjukuSha256": "1406c0d44399772d43e33dc6b4cfb7428f9f24215e13389ac46f5dd6dc28d224",
6
+ "shinjukuRelease": "bfaedd12",
7
+ "shinjukuSha256": "bfaedd12d120b4b5421cdf6c9c06b07b7e338fd6d4e74b7225f2a0d3aa150cf4",
8
8
  "license": "SEE LICENSE IN LICENSE.txt",
9
9
  "author": "Shinjuku Shielded",
10
10
  "homepage": "https://github.com/ShinjukuStaition/shinjuku-shield#readme",
@@ -67087,6 +67087,7 @@ var init_errorCodes = __esm({
67087
67087
  "p03_float_pool_unreadable_retry_same_request",
67088
67088
  "p03_float_body_too_large",
67089
67089
  "p03_float_busy_retry_later",
67090
+ "p03_float_fee_account_low",
67090
67091
  "p03_float_not_ready_retry_later",
67091
67092
  "p03_float_payment_in_flight_retry_later",
67092
67093
  "p03_float_already_paid",
@@ -81130,7 +81131,7 @@ function privateSendHold(store, { accounts, notes, selfProofWalName } = {}) {
81130
81131
  const self2 = typeof selfProofWalName === "string" ? (part) => selfProofWalName === `resource-proof-${sha256Hex2(Buffer.from(selfPayRequestId(part.unshieldId), "utf8"))}` : void 0;
81131
81132
  const unsent = unsentParts(store, self2);
81132
81133
  if (unsent === null) return null;
81133
- const hold = { accounts: /* @__PURE__ */ new Set(), notes: /* @__PURE__ */ new Set(), sends: [], unpacked: [], spareAtomic: "0" };
81134
+ const hold = { accounts: /* @__PURE__ */ new Map(), notes: /* @__PURE__ */ new Set(), sends: [], unpacked: [] };
81134
81135
  if (unsent.length === 0) return hold;
81135
81136
  try {
81136
81137
  unsent.sort(bySendPart);
@@ -81148,22 +81149,27 @@ function privateSendHold(store, { accounts, notes, selfProofWalName } = {}) {
81148
81149
  const { bins } = packed;
81149
81150
  const unpacked = unsent.filter(({ plan }) => short.has(plan.sendId));
81150
81151
  const holding = /* @__PURE__ */ new Set();
81151
- let spare = 0n;
81152
+ const owed = /* @__PURE__ */ new Map();
81153
+ for (const bin of bins) for (const entry of bin.parts) owed.set(entry.plan.sendId, (owed.get(entry.plan.sendId) ?? 0n) + BigInt(entry.part.amountAtomic));
81152
81154
  for (const bin of bins) {
81153
81155
  if (bin.parts.length === 0 || bin.parts.some((entry) => entry.self)) continue;
81154
- hold.accounts.add(BigInt(bin.entry.commitment).toString());
81156
+ const need = [...new Set(bin.parts.map((entry) => entry.plan.sendId))].reduce((sum2, sendId) => sum2 + owed.get(sendId), 0n);
81157
+ const account = BigInt(bin.entry.account.b);
81158
+ hold.accounts.set(BigInt(bin.entry.commitment).toString(), (bin.notes.length > 0 || need > account ? account : need).toString());
81155
81159
  for (const note of bin.notes) hold.notes.add(BigInt(note.commitment).toString());
81156
81160
  for (const entry of bin.parts) holding.add(entry.plan.sendId);
81157
- spare += bin.value - bin.used;
81158
81161
  }
81159
81162
  for (const { plan } of unsent) if (holding.delete(plan.sendId)) hold.sends.push(namedSend(plan));
81160
81163
  hold.unpacked = unpacked.map(({ plan, part }) => ({ sendId: plan.sendId, index: part.index, amountAtomic: String(part.amountAtomic) }));
81161
- hold.spareAtomic = spare.toString();
81162
81164
  } catch {
81163
81165
  return null;
81164
81166
  }
81165
81167
  return hold;
81166
81168
  }
81169
+ function heldSpareAtomic(hold, entry) {
81170
+ const need = hold.accounts.get(BigInt(entry.commitment).toString());
81171
+ return need === void 0 ? void 0 : BigInt(entry.account.b) - BigInt(need);
81172
+ }
81167
81173
  function privateSendPartsFit(store, { accounts, notes, partAmounts: partAmounts2 }) {
81168
81174
  const unsent = unsentParts(store);
81169
81175
  if (unsent === null) return false;
@@ -81373,7 +81379,11 @@ var init_privateSend = __esm({
81373
81379
  DIRECT_EXIT_MODE = "direct-exit";
81374
81380
  noteFitsAccount = (account, note) => account.account?.i === void 0 || note.leafIndex === void 0 || note.leafIndex === null || BigInt(note.leafIndex) >= BigInt(account.account.i);
81375
81381
  bySendPart = (left, right) => left.plan.sendId < right.plan.sendId ? -1 : left.plan.sendId > right.plan.sendId ? 1 : left.part.index - right.part.index;
81376
- namedSend = (plan) => typeof plan.transferTokenId === "string" ? { sendId: plan.sendId, requestId: plan.transferTokenId, tool: "wallet_transfer_token" } : typeof plan.mcpTo !== "string" ? { sendId: plan.sendId, requestId: plan.sendId, tool: "cli" } : { sendId: plan.sendId, requestId: typeof plan.mcpRequestId === "string" ? plan.mcpRequestId : String(plan.sendId).replace(/^mcp-/, ""), tool: "wallet_unshield" };
81382
+ namedSend = (plan) => ({
81383
+ ...typeof plan.transferTokenId === "string" ? { sendId: plan.sendId, requestId: plan.transferTokenId, tool: "wallet_transfer_token" } : typeof plan.mcpTo !== "string" ? { sendId: plan.sendId, requestId: plan.sendId, tool: "cli" } : { sendId: plan.sendId, requestId: typeof plan.mcpRequestId === "string" ? plan.mcpRequestId : String(plan.sendId).replace(/^mcp-/, ""), tool: "wallet_unshield" },
81384
+ to: typeof plan.mcpTo === "string" ? plan.mcpTo : plan.to,
81385
+ amountAtomic: String(plan.amountAtomic)
81386
+ });
81377
81387
  cancelledPart = (store, plan, part) => plan.state === "cancelled" && !partMayHaveSigned(store, part);
81378
81388
  partCancelled = (store, sendId, part) => {
81379
81389
  const plan = store.readWal(privateSendPlanName(sendId));
@@ -81714,7 +81724,9 @@ async function everyAccountHeld({ store, connection, programId, serviceUrl: serv
81714
81724
  if (freeAccounts.length === 0) return anyHeld;
81715
81725
  const largest = freeAccounts.reduce((max, entry) => BigInt(entry.account.b) > BigInt(max.account.b) ? entry : max);
81716
81726
  if (!anyHeld) {
81717
- assertAccountsNotSplit({ accounts: freeAccounts, account: largest, notes: freeNotes.filter(free), amountAtomic });
81727
+ if (!unheldAccounts.some((entry) => (heldSpareAtomic(sendHold, entry) ?? 0n) >= BigInt(amountAtomic))) {
81728
+ assertAccountsNotSplit({ accounts: freeAccounts, account: largest, notes: freeNotes.filter(free), amountAtomic });
81729
+ }
81718
81730
  return false;
81719
81731
  }
81720
81732
  const notesValue = freeNotes.reduce((sum2, entry) => sum2 + BigInt(entry.note.b), 0n);
@@ -81872,10 +81884,15 @@ async function prepareRecoveredPayment({
81872
81884
  };
81873
81885
  const unheldNotes = unspentNotes.filter((entry) => !heldByPending(entry) && !exitReserved.has(BigInt(entry.commitment).toString()) && !keptNote(entry));
81874
81886
  const held = unheldAccounts.length !== unspentAccounts.length || unspentNotes.filter((entry) => !exitReserved.has(BigInt(entry.commitment).toString()) && !keptNote(entry)).length !== unheldNotes.length;
81875
- const sendHold = deps.poolMove === true ? { accounts: /* @__PURE__ */ new Set(), notes: /* @__PURE__ */ new Set(), sends: [], unpacked: [], spareAtomic: "0" } : privateSendHold(store, { accounts: unheldAccounts, notes: unheldNotes, selfProofWalName: requestedWalName });
81887
+ const sendHold = deps.poolMove === true ? { accounts: /* @__PURE__ */ new Map(), notes: /* @__PURE__ */ new Set(), sends: [], unpacked: [] } : privateSendHold(store, { accounts: unheldAccounts, notes: unheldNotes, selfProofWalName: requestedWalName });
81876
81888
  if (sendHold === null) heldByPrivateSend([]);
81877
81889
  const sendHeld = (entry) => (entry.kind === "account" ? sendHold.accounts : sendHold.notes).has(BigInt(entry.commitment).toString());
81878
81890
  const accounts = sendHold.accounts.size === 0 ? unheldAccounts : unheldAccounts.filter((entry) => !sendHeld(entry));
81891
+ const heldAccounts = sendHold.accounts.size === 0 ? [] : unheldAccounts.filter(sendHeld);
81892
+ const spareSpend = (outputIndex) => {
81893
+ const fits = heldAccounts.filter((entry) => heldSpareAtomic(sendHold, entry) >= BigInt(witnessQuote.amountAtomic));
81894
+ return fits.length === 0 ? void 0 : selectSpendAccount({ accounts: fits, notes: [], outputIndex, amountAtomic: witnessQuote.amountAtomic }).account;
81895
+ };
81879
81896
  const freeNotes = sendHold.notes.size === 0 ? unheldNotes : unheldNotes.filter((entry) => !sendHeld(entry));
81880
81897
  const notes = sendHold.accounts.size === 0 && sendHold.notes.size === 0 ? unheldNotes : freeNotes.filter((entry) => accounts.some((account2) => noteFitsAccount(account2, entry)));
81881
81898
  let refusedValues;
@@ -81887,7 +81904,7 @@ async function prepareRecoveredPayment({
81887
81904
  return false;
81888
81905
  }
81889
81906
  };
81890
- const wantedNotes = selection?.noteCommitments !== void 0 ? selection.noteCommitments.map((commitment) => {
81907
+ let wantedNotes = selection?.noteCommitments !== void 0 ? selection.noteCommitments.map((commitment) => {
81891
81908
  const wanted = BigInt(commitment);
81892
81909
  const found = notes.find((entry) => BigInt(entry.commitment) === wanted);
81893
81910
  if (!found && exitReserved.has(wanted.toString())) fail20("requested_note_reserved_by_exit");
@@ -81982,14 +81999,21 @@ async function prepareRecoveredPayment({
81982
81999
  if (largest && finalizedLargest !== void 0 && (underAnchor === void 0 || BigInt(finalizedLargest.account.b) > BigInt(underAnchor.account.b))) {
81983
82000
  throw new Error("p03_quote_anchor_older_than_note_wait_next_epoch");
81984
82001
  }
81985
- account = underAnchor ?? accounts[0];
81986
- sendShort = (sendHold.accounts.size > 0 || sendHold.notes.size > 0) && !pays(underAnchor);
82002
+ const spareAllowed = !pays(underAnchor) && !largest && selection?.noteCommitments === void 0;
82003
+ const spare = spareAllowed ? spareSpend(anchor.index) : void 0;
82004
+ if (spareAllowed && spare === void 0 && spareSpend(history.finalizedPoolSnapshot.currentIndex) !== void 0) {
82005
+ throw new Error("p03_quote_anchor_older_than_note_wait_next_epoch");
82006
+ }
82007
+ if (spare !== void 0) wantedNotes = [];
82008
+ account = spare ?? underAnchor ?? accounts[0];
82009
+ sendShort = spare === void 0 && (sendHold.accounts.size > 0 || sendHold.notes.size > 0) && !pays(underAnchor);
81987
82010
  }
81988
82011
  if (account) assertAccountsNotSplit({ accounts, account, notes: wantedNotes, allNotes: notes, amountAtomic: witnessQuote.amountAtomic });
81989
- if (sendShort && (accounts.length === 0 || [...accounts.map((entry) => entry.account.b), ...freeNotes.map((entry) => entry.note.b)].reduce((sum2, value2) => sum2 + BigInt(value2), BigInt(sendHold.spareAtomic)) >= BigInt(witnessQuote.amountAtomic))) {
82012
+ if (sendShort && (accounts.length === 0 || [...accounts.map((entry) => entry.account.b), ...freeNotes.map((entry) => entry.note.b)].reduce((sum2, value2) => sum2 + BigInt(value2), heldAccounts.reduce((sum2, entry) => sum2 + heldSpareAtomic(sendHold, entry), 0n)) >= BigInt(witnessQuote.amountAtomic))) {
81990
82013
  heldByPrivateSend(sendHold.sends);
81991
82014
  }
81992
82015
  if (!account) fail20("no_spendable_account");
82016
+ if (sendHeld(account) && heldSpareAtomic(sendHold, account) + wantedNotes.reduce((sum2, note) => sum2 + BigInt(note.note.b), 0n) < BigInt(witnessQuote.amountAtomic)) heldByPrivateSend(sendHold.sends);
81993
82017
  await prepareFold(deps.buildRecoveryPath);
81994
82018
  accountPath = buildRecoveryPath({
81995
82019
  history,
@@ -100854,6 +100878,14 @@ function floatOutcomeWords(code2, error, t) {
100854
100878
  return [code2, "Shinjuku's shared float is busy right now; nothing was paid", t.againIn(error?.retryAfterSeconds ?? 30)];
100855
100879
  case "p03_float_not_ready_retry_later":
100856
100880
  return [code2, "Shinjuku's shared float is not ready yet (it is being filled); nothing was paid", t.againIn(error?.retryAfterSeconds ?? 60)];
100881
+ // The fee payer of Shinjuku's shared pockets is low on SOL (floatPay.mjs floatRefusalError, 0.3.113): only its operator
100882
+ // refills it, so the words name no time and ask for no retry soon (mcp6 0.3.112 gate: "retry in a minute" failed again).
100883
+ case "p03_float_fee_account_low":
100884
+ return [
100885
+ code2,
100886
+ "the Shinjuku account that pays the network fee of this payment is low on SOL, and its operator was alerted; nothing was paid and the balance did not change",
100887
+ `This is not a fault of the wallet: the payment can go after the operator adds SOL to that account, and no time is known. ${t.rerun} later`
100888
+ ];
100857
100889
  case "p03_float_payment_in_flight_retry_later":
100858
100890
  return [code2, "an earlier quote for this payment is still open at Shinjuku; nothing was paid", t.againIn(error?.retryAfterSeconds ?? 60)];
100859
100891
  case "p03_float_price_above_capacity":
@@ -101870,6 +101902,8 @@ function computeBalance({ store, snapshotRead, source = "local", now = Date.now(
101870
101902
  let spendInputs;
101871
101903
  let sendHold;
101872
101904
  let heldAtomic;
101905
+ let heldWholeAtomic;
101906
+ let spareMost = 0n;
101873
101907
  const report = (status, gross, next2, cause, largest2, change) => {
101874
101908
  const atomic4 = gross === null || privateSendPending === null ? null : gross > privateSendPending ? gross - privateSendPending : 0n;
101875
101909
  const built = {
@@ -101899,7 +101933,11 @@ function computeBalance({ store, snapshotRead, source = "local", now = Date.now(
101899
101933
  if (spendInputs !== void 0) SPEND_INPUTS.set(built, spendInputs);
101900
101934
  if (atomic4 !== null && sendHold && heldAtomic !== void 0) {
101901
101935
  const aside = heldAtomic > privateSendPending ? heldAtomic : privateSendPending;
101902
- SEND_HOLDS.set(built, { ...sendHold, freeAtomic: (gross > aside ? gross - aside : 0n).toString(), heldAtomic: aside.toString() });
101936
+ const whole = heldWholeAtomic > privateSendPending ? heldWholeAtomic : privateSendPending;
101937
+ const outside = gross > whole ? gross - whole : 0n;
101938
+ const most = outside > spareMost ? outside : spareMost;
101939
+ const cap = gross > aside ? gross - aside : 0n;
101940
+ SEND_HOLDS.set(built, { ...sendHold, freeAtomic: (most < cap ? most : cap).toString(), heldAtomic: aside.toString() });
101903
101941
  }
101904
101942
  if (largest2 !== void 0 && atomic4 !== null) {
101905
101943
  const cap = atomic4 - BigInt(detail.successionMovableAtomic ?? "0");
@@ -102158,14 +102196,18 @@ It asks the amount, then gives a one-time address and its QR code: send USDC to
102158
102196
  const entries = free.map(spendEntry);
102159
102197
  const freeInputs = { accounts: entries.filter((entry) => entry.kind === "account"), notes: entries.filter((entry) => entry.kind === "note") };
102160
102198
  if (!unknownInputs) spendInputs = freeInputs;
102161
- sendHold = privateSendPending === 0n ? { accounts: /* @__PURE__ */ new Set(), notes: /* @__PURE__ */ new Set(), sends: [], unpacked: [] } : privateSendPending === null ? null : privateSendHold(store, freeInputs);
102199
+ sendHold = privateSendPending === 0n ? { accounts: /* @__PURE__ */ new Map(), notes: /* @__PURE__ */ new Set(), sends: [], unpacked: [] } : privateSendPending === null ? null : privateSendHold(store, freeInputs);
102162
102200
  const isHeld = (output2) => sendHold.accounts.has(output2.commitment) || sendHold.notes.has(output2.commitment);
102163
102201
  const notHeld = sendHold === null ? [] : free.filter((output2) => !isHeld(output2));
102164
102202
  const open4 = sendHold === null || sendHold.accounts.size === 0 && sendHold.notes.size === 0 ? notHeld : notHeld.filter((output2) => output2.kind !== "note" || notHeld.some((other) => other.kind === "account" && noteFitsAccount(spendEntry(other), spendEntry(output2))));
102203
+ const spares = sendHold === null ? [] : free.filter((output2) => output2.kind === "account" && sendHold.accounts.has(output2.commitment)).map((output2) => ({ output: output2, spareAtomic: heldSpareAtomic(sendHold, spendEntry(output2)) }));
102165
102204
  if (sendHold !== null) {
102166
- heldAtomic = sum2(free.filter(isHeld).map((output2) => output2.valueAtomic)) + sum2(sendHold.unpacked.map((part) => part.amountAtomic));
102205
+ heldWholeAtomic = sum2(free.filter(isHeld).map((output2) => output2.valueAtomic)) + sum2(sendHold.unpacked.map((part) => part.amountAtomic));
102206
+ heldAtomic = heldWholeAtomic - sum2(spares.map((spare) => spare.spareAtomic));
102207
+ spareMost = spares.reduce((most, spare) => spare.spareAtomic > most ? spare.spareAtomic : most, 0n);
102167
102208
  }
102168
- const largest = sendHold === null ? void 0 : largestOneSpend(open4);
102209
+ const openLargest = sendHold === null ? void 0 : largestOneSpend(open4);
102210
+ const largest = openLargest === void 0 || openLargest >= spareMost ? openLargest : spareMost;
102169
102211
  if (settling.some((payment) => payment.inputs.some((commitment) => reserved.has(commitment) || joining.has(commitment))) || unknownInputs && settling.length) {
102170
102212
  reasons.push(...settlingReasons);
102171
102213
  return report("stale", null, refreshCommand(state, snapshot.programId));
@@ -102176,7 +102218,10 @@ It asks the amount, then gives a one-time address and its QR code: send USDC to
102176
102218
  return report("stale", null, refreshCommand(state, snapshot.programId));
102177
102219
  }
102178
102220
  const anchorIndex = canonicalAtomic(snapshot.anchorIndex) ? BigInt(snapshot.anchorIndex) : void 0;
102179
- detail.fundedAccounts = unknownInputs ? 0 : sendHold === null ? null : open4.filter((output2) => output2.kind === "account" && BigInt(output2.valueAtomic) > 0n && (anchorIndex === void 0 || BigInt(output2.leafIndex) < anchorIndex)).length;
102221
+ detail.fundedAccounts = unknownInputs ? 0 : sendHold === null ? null : [
102222
+ ...open4.filter((output2) => output2.kind === "account" && BigInt(output2.valueAtomic) > 0n),
102223
+ ...spares.filter((spare) => spare.spareAtomic > 0n).map((spare) => spare.output)
102224
+ ].filter((output2) => anchorIndex === void 0 || BigInt(output2.leafIndex) < anchorIndex).length;
102180
102225
  const answeredOnly = detail.inFlight.length > 0 && detail.inFlight.every((item) => item.answered === true);
102181
102226
  const next = answeredOnly ? detail.inFlight.find((item) => item.next.startsWith("rerun"))?.next ?? "nothing to do: an answered unshield finishes its check by itself" : detail.inFlight.length ? "finish or cancel the work in detail.inFlight; the wallet runs one payment at a time" : pendingDeposits.length ? byAddFunds(pendingDeposits[0].depositId) ? `deposit ${pendingDeposits[0].depositId} of add-funds is not finished. ${continueAddFunds}` : `deposit ${pendingDeposits[0].depositId} is not finished: rerun the same deposit command with --submit --service <facilitator URL>` : pocketsUnscanned ? scanPockets2 : unspentAtomic === 0n && successionMovableAtomic > 0n ? "this pool was succeeded by a new pool: its value moves there (one move per funded deposit, the facilitator pays the fee) and counts in this balance until then" : unspentAtomic === 0n ? `nothing to spend. To add money, run:
102182
102227
  ${commandLine("add-funds")}
@@ -102188,9 +102233,11 @@ If a payment to this wallet settled, read the chain again with:
102188
102233
  if (settling.length || movedInAtomic > 0n) {
102189
102234
  reasons.push(...settlingReasons);
102190
102235
  const [only] = settling;
102191
- const sendHeldAtomic = sendHold === null || sendHold.unpacked.length > 0 || sendHold.notes.size > 0 ? null : sum2(free.filter(isHeld).map((output2) => output2.valueAtomic));
102236
+ const sendHeldAtomic = sendHold === null || sendHold.notes.size > 0 ? null : sum2(free.filter(isHeld).map((output2) => output2.valueAtomic));
102192
102237
  const inputsAtomic = settling.length === 1 ? sum2(only.inputs.map((commitment) => unspent.get(commitment).valueAtomic)) : 0n;
102193
- const changeOnly = settling.length === 1 && only.join !== true && only.exit !== true && reservedAtomic === 0n && detail.joiningAtomic === "0" && successionMovableAtomic === 0n && sendHeldAtomic !== null && unspentAtomic - sendHeldAtomic === inputsAtomic && inputsAtomic > only.amountAtomic;
102238
+ const changeAtomic = inputsAtomic - (settling.length === 1 ? only.amountAtomic : 0n);
102239
+ const changeFreeAtomic = sendHold === null || sendHold.unpacked.length === 0 ? changeAtomic : changeAtomic > privateSendPending ? changeAtomic - privateSendPending : 0n;
102240
+ const changeOnly = settling.length === 1 && only.join !== true && only.exit !== true && reservedAtomic === 0n && detail.joiningAtomic === "0" && successionMovableAtomic === 0n && sendHeldAtomic !== null && unspentAtomic - sendHeldAtomic === inputsAtomic && inputsAtomic > only.amountAtomic && changeFreeAtomic > 0n;
102194
102241
  return report(
102195
102242
  "stale",
102196
102243
  spendable + successionMovableAtomic,
@@ -102199,7 +102246,7 @@ If a payment to this wallet settled, read the chain again with:
102199
102246
  // still settling" while the only answered item was a join).
102200
102247
  settlingRecent ? movedInAtomic === 0n && settling.length > 0 && settling.every((payment) => payment.join) ? "account_join" : "payment_settling" : movedInAtomic > 0n ? "pool_move" : void 0,
102201
102248
  largest,
102202
- changeOnly ? inputsAtomic - only.amountAtomic : void 0
102249
+ changeOnly ? changeFreeAtomic : void 0
102203
102250
  );
102204
102251
  }
102205
102252
  return report("current", spendable + successionMovableAtomic, next, void 0, largest);
@@ -102214,7 +102261,7 @@ function savedSpendInputs({ store, snapshotRead }) {
102214
102261
  }
102215
102262
  function savedSendHold({ store, snapshotRead }) {
102216
102263
  try {
102217
- if (privateSendPendingAtomic(store) === 0n) return { accounts: /* @__PURE__ */ new Set(), notes: /* @__PURE__ */ new Set(), sends: [], unpacked: [] };
102264
+ if (privateSendPendingAtomic(store) === 0n) return { accounts: /* @__PURE__ */ new Map(), notes: /* @__PURE__ */ new Set(), sends: [], unpacked: [] };
102218
102265
  const report = computeBalance({ store, snapshotRead: snapshotRead ?? readBalanceSnapshot(store) });
102219
102266
  return report.status === "current" && SPEND_INPUTS.has(report) ? sendHoldOf(report) ?? null : null;
102220
102267
  } catch {
@@ -103214,6 +103261,7 @@ function floatFetch(base, seen) {
103214
103261
  code: code2,
103215
103262
  ...Number.isFinite(retryAfter) && retryAfter > 0 ? { retryAfterSeconds: Math.min(Math.ceil(retryAfter), 3600) } : {},
103216
103263
  ...DECIMAL3.test(String(answer.largestAtomic ?? "")) ? { largestAtomic: String(answer.largestAtomic) } : {},
103264
+ ...typeof answer.reason === "string" && /^[a-z_]{1,60}$/.test(answer.reason) ? { reason: answer.reason } : {},
103217
103265
  ...typeof answer.message === "string" ? { message: plainText(answer.message) } : {}
103218
103266
  };
103219
103267
  return new Response(null, { status: 400 });
@@ -103232,8 +103280,9 @@ function floatRefusalError(refusal3, record) {
103232
103280
  return Object.assign(new Error("p03_float_busy_retry_later"), { retryAfterSeconds: 30, ...extra });
103233
103281
  case "float_price_above_capacity":
103234
103282
  return Object.assign(new Error("p03_float_price_above_capacity"), extra);
103283
+ // fee_payer_underfunded (0.3.113): the fee payer of the shared pockets needs the operator's SOL, so no retry soon helps.
103235
103284
  case "float_not_ready":
103236
- return Object.assign(new Error("p03_float_not_ready_retry_later"), { retryAfterSeconds: 60, ...extra });
103285
+ return refusal3.reason === "fee_payer_underfunded" ? Object.assign(new Error("p03_float_fee_account_low"), extra) : Object.assign(new Error("p03_float_not_ready_retry_later"), { retryAfterSeconds: 60, ...extra });
103237
103286
  case "float_payment_in_flight":
103238
103287
  return Object.assign(new Error("p03_float_payment_in_flight_retry_later"), { retryAfterSeconds: 60, ...extra });
103239
103288
  case "float_already_paid":
@@ -103523,7 +103572,7 @@ async function payViaFloat({
103523
103572
  };
103524
103573
  store.writeWal(walName2, record);
103525
103574
  mark("seller-sent");
103526
- const sentAtMs = Date.now();
103575
+ const sentAtMs2 = Date.now();
103527
103576
  const stopWaiting = new AbortController();
103528
103577
  const watch = watchFloatTransfer({ record, fetchImpl, sleep: deps.floatStatusSleep, abort: () => stopWaiting.abort() });
103529
103578
  const resendDispatchers = [];
@@ -103538,7 +103587,7 @@ async function payViaFloat({
103538
103587
  if (wasKept) keptAgain();
103539
103588
  const verdict = watch.verdict();
103540
103589
  if (verdict?.state === "dead") {
103541
- const sellerUnansweredSeconds = Math.round((Date.now() - sentAtMs) / 1e3);
103590
+ const sellerUnansweredSeconds = Math.round((Date.now() - sentAtMs2) / 1e3);
103542
103591
  const { ambiguous: _settled, ...rest } = record;
103543
103592
  record = {
103544
103593
  ...rest,
@@ -103680,7 +103729,7 @@ async function payViaFloat({
103680
103729
  ...state?.state === "landed" ? { transferLanded: true } : {}
103681
103730
  });
103682
103731
  }
103683
- const late2 = x402Header(response, "payment-response")?.success === false ? { sellerChargeFailed: true, sellerAnswerSeconds: Math.round((answeredAtMs - sentAtMs) / 1e3) } : {};
103732
+ const late2 = x402Header(response, "payment-response")?.success === false ? { sellerChargeFailed: true, sellerAnswerSeconds: Math.round((answeredAtMs - sentAtMs2) / 1e3) } : {};
103684
103733
  const fixable = floatSellerCanBeFixed({ httpStatus: response.status, ...sellerReason ? { sellerReason } : {}, ...late2 }) ? { sellerFixable: true } : {};
103685
103734
  byVerdict(
103686
103735
  await floatVerdict(record, fetchImpl, statusSleep, verdictUntil(answeredAtMs), watch.verdict()),
@@ -106348,28 +106397,31 @@ function promisedShortfall(store, amountAtomic) {
106348
106397
  }
106349
106398
  return void 0;
106350
106399
  }
106351
- function heldByPrivateSendText(sends) {
106400
+ function heldByPrivateSendText(sends, freeAtomic) {
106352
106401
  const named = Array.isArray(sends) ? sends.filter((send3) => typeof send3?.requestId === "string") : [];
106402
+ const most = /^\d+$/.test(String(freeAtomic ?? "")) ? `One payment can use at most ${usdcWords(freeAtomic)} now, and this payment needs more. ` : "One payment cannot use this much now. ";
106403
+ const frees = "keeps nothing after its last part goes; an earlier part may free some money, and wallet_balance shows the most one payment can use (largestPaymentAtomic).";
106353
106404
  if (named.length === 0) {
106354
- return "This payment needs money that is not free now: a private send in progress has set this money aside; wallet_balance names it and its cancel call. The wallet frees it when the next part of that send goes. To pay now, stop the send, or add money with wallet_add_funds.";
106405
+ return `${most}A private send in progress keeps part of this money for its unsent parts; wallet_balance names it and its cancel call. The send ${frees} To pay now, stop the send, or add money with wallet_add_funds.`;
106355
106406
  }
106356
106407
  if (named.length === 1) {
106357
- return `This payment needs money that the private send "${named[0].requestId}" has set aside for its next part. The wallet frees it when that part goes. To pay now, stop the send with ${cancelCall(named[0])}, or add money with wallet_add_funds.`;
106408
+ const send3 = sendWords(named[0]);
106409
+ return `${most}${send3[0].toUpperCase()}${send3.slice(1)} keeps part of this money for its unsent parts. It ${frees} To pay now, stop the send with ${cancelCall(named[0])}, or add money with wallet_add_funds.`;
106358
106410
  }
106359
- return `This payment needs money that the private sends ${named.map((send3) => `"${send3.requestId}"`).join(" and ")} have set aside for their next parts. The wallet frees it when those parts go. To pay now, stop a send with ${named.map(cancelCall).join(", or ")}, or add money with wallet_add_funds.`;
106411
+ return `${most}${named.map(sendWords).join(" and ").replace(/^t/, "T")} keep part of this money for their unsent parts. Each ${frees} To pay now, stop a send with ${named.map(cancelCall).join(", or ")}, or add money with wallet_add_funds.`;
106360
106412
  }
106361
106413
  function promisedShortfallText(shortfall, amountAtomic) {
106362
106414
  if (shortfall.error === "spend_private_send_unknown") {
106363
106415
  return "spend_private_send_unknown: a private send of this wallet cannot be read, so the value it still owes is unknown; nothing was sent";
106364
106416
  }
106365
- if (shortfall.error === HELD_BY_PRIVATE_SEND) return `${HELD_BY_PRIVATE_SEND}: ${heldByPrivateSendText(shortfall.privateSends)} Nothing was sent.`;
106417
+ if (shortfall.error === HELD_BY_PRIVATE_SEND) return `${HELD_BY_PRIVATE_SEND}: ${heldByPrivateSendText(shortfall.privateSends, shortfall.freeAtomic)} Nothing was sent.`;
106366
106418
  return `spend_private_send_reserved: ${usdcWords(shortfall.pendingAtomic)} of the private balance is promised to the unsent smaller payments of a private send; ${usdcWords(shortfall.freeAtomic)} is free and this unshield needs ${usdcWords(amountAtomic)}; nothing was sent`;
106367
106419
  }
106368
106420
  function assertUnshieldNotPromised(store, amountAtomic) {
106369
106421
  const shortfall = promisedShortfall(store, amountAtomic);
106370
106422
  if (shortfall) throw Object.assign(new Error(promisedShortfallText(shortfall, amountAtomic)), shortfall.privateSends ? { privateSends: shortfall.privateSends } : {});
106371
106423
  }
106372
- var HELD_BY_PRIVATE_SEND, cancelCall;
106424
+ var HELD_BY_PRIVATE_SEND, cancelCall, sendWords;
106373
106425
  var init_promised = __esm({
106374
106426
  "wallet/src/unshield/promised.mjs"() {
106375
106427
  "use strict";
@@ -106378,6 +106430,11 @@ var init_promised = __esm({
106378
106430
  init_usdcWords();
106379
106431
  HELD_BY_PRIVATE_SEND = "p03_merchant_respend_value_held_by_private_send";
106380
106432
  cancelCall = (send3) => send3.tool === "wallet_transfer_token" ? `wallet_transfer_token_status with request_id "${send3.requestId}" and action "cancel"` : send3.tool === "cli" ? `the command line: unshield-cancel --unshield-id ${send3.sendId}` : `wallet_cancel with request_id "${send3.requestId}"`;
106433
+ sendWords = (send3) => {
106434
+ const amount = /^[1-9][0-9]*$/.test(String(send3?.amountAtomic ?? "")) ? ` of ${usdcWords(send3.amountAtomic)}` : "";
106435
+ const to = typeof send3?.to === "string" && send3.to.length > 10 ? ` to ${send3.to.slice(0, 4)}...${send3.to.slice(-4)}` : "";
106436
+ return `the private send${amount}${to}`;
106437
+ };
106381
106438
  }
106382
106439
  });
106383
106440
 
@@ -106767,7 +106824,7 @@ async function assertNotPromised({ session, store, amountAtomic, leasedAt, mark,
106767
106824
  ok: false,
106768
106825
  error: "spend_private_send_reserved",
106769
106826
  detail: `${usdcWords(pending)} of the private balance is promised to the unsent smaller payments of a private send; ${usdcWords(free)} is free and this payment needs ${usdcWords(amountAtomic)}; nothing was paid`,
106770
- next: wait2
106827
+ next: "Tell the user why nothing was paid and what they can do. This payment needs more than the money the private send does not keep, and the money it keeps leaves the wallet as its parts go, so a wait frees nothing. Stop its unsent parts with the cancel call wallet_balance names for it (that money stays in the wallet), or add money with wallet_add_funds, then pay again with the same request_id"
106771
106828
  });
106772
106829
  }
106773
106830
  const hold = sendHoldOf(report);
@@ -106775,8 +106832,8 @@ async function assertNotPromised({ session, store, amountAtomic, leasedAt, mark,
106775
106832
  throw Object.assign(new SpendRefusalError({
106776
106833
  ok: false,
106777
106834
  error: HELD_BY_PRIVATE_SEND,
106778
- detail: `${heldByPrivateSendText(hold.sends)} Nothing was paid.`,
106779
- next: "Tell the user why nothing was paid and what they can do. After the next part of the send goes, after a stop of the send, or after new money arrives, pay again with the same request_id",
106835
+ detail: `${heldByPrivateSendText(hold.sends, hold.freeAtomic)} Nothing was paid.`,
106836
+ next: "Tell the user why nothing was paid and what they can do. One payment cannot use this much now beside what the private send keeps for its unsent parts. The send keeps nothing after its last part goes, and an earlier part may free some money: pay again with the same request_id when wallet_balance largestPaymentAtomic covers this payment, after a stop of the send, or after new money is private",
106780
106837
  privateSends: hold.sends
106781
106838
  }), { privateSends: hold.sends });
106782
106839
  }
@@ -108801,12 +108858,14 @@ function heldBySendLine(error) {
108801
108858
  for (let link2 = error, depth = 0; link2 instanceof Error && depth < 8; link2 = link2.cause, depth += 1) {
108802
108859
  if (!String(link2.message).startsWith(HELD_BY_PRIVATE_SEND)) continue;
108803
108860
  const sends = Array.isArray(link2.privateSends) ? link2.privateSends.filter((one) => typeof one?.requestId === "string" && typeof one.sendId === "string") : [];
108861
+ const frees = "It keeps nothing after its last part goes; an earlier part may free some money (shinjuku balance shows it).";
108804
108862
  if (sends.length === 0) {
108805
- return "Nothing was sent. This send needs money that a private send in progress has set aside. The wallet frees it when that send's next part goes. To send now, stop that send (shinjuku unshield-cancel --unshield-id <its id>; a token transfer: shinjuku transfer-token --id <its id> --cancel), or add money with shinjuku add-funds.";
108863
+ return `Nothing was sent. One payment cannot use this much now. A private send in progress keeps part of this money for its unsent parts. ${frees} To send now, stop that send (shinjuku unshield-cancel --unshield-id <its id>; a token transfer: shinjuku transfer-token --id <its id> --cancel), or add money with shinjuku add-funds.`;
108806
108864
  }
108807
108865
  const many = sends.length > 1;
108808
- const stops = sends.map((one) => one.tool === "wallet_transfer_token" ? `shinjuku transfer-token --id ${one.requestId} --cancel` : `shinjuku unshield-cancel --unshield-id ${one.sendId}`);
108809
- return `Nothing was sent. This send needs money that the private send${many ? "s" : ""} ${sends.map((one) => `"${one.requestId}"`).join(" and ")} ${many ? "have" : "has"} set aside for ${many ? "their next parts" : "its next part"}. The wallet frees it when ${many ? "those parts go" : "that part goes"}. To send now, stop the send with ${stops.join(", or ")}, or add money with shinjuku add-funds.`;
108866
+ const stops = sends.map((one) => `${one.tool === "wallet_transfer_token" ? `shinjuku transfer-token --id ${one.requestId} --cancel` : `shinjuku unshield-cancel --unshield-id ${one.sendId}`} (${sendWords(one)})`);
108867
+ const named = sends.map(sendWords).join(" and ");
108868
+ return `Nothing was sent. One payment cannot use this much now. ${named[0].toUpperCase()}${named.slice(1)} ${many ? "keep" : "keeps"} part of this money for ${many ? "their" : "its"} unsent parts. ${many ? frees.replace(/^It keeps/, "Each keeps") : frees} To send now, stop the send with ${stops.join(", or ")}, or add money with shinjuku add-funds.`;
108810
108869
  }
108811
108870
  return void 0;
108812
108871
  }
@@ -119528,6 +119587,7 @@ function refusalSay(a, tool, again, npmVersion) {
119528
119587
  if (/_retry_same_request$/.test(code2)) {
119529
119588
  return a.paid === true ? `Paid, but the seller has not answered yet. ${again}` : `The result is not known yet. ${again}`;
119530
119589
  }
119590
+ if (code2 === "p03_float_fee_account_low") return "Nothing was paid. The payment service's network fee account is low; its operator was alerted. Try again later.";
119531
119591
  if (BUSY.test(code2)) {
119532
119592
  const said = /in about ([0-9]{1,4}) s\b/.exec(next);
119533
119593
  const seconds2 = Number.isFinite(a.readyInSeconds) && a.readyInSeconds > 0 ? Math.ceil(a.readyInSeconds) : said ? Number(said[1]) : 30;
@@ -119987,6 +120047,7 @@ function previewReason(a, npmVersion) {
119987
120047
  return raiseLine("--max-session", npmVersion, `over your 24-hour limit of ${usdcText2(a.capAtomic)} USDC.`, SAY_MAX_WORDS - 3);
119988
120048
  }
119989
120049
  if (OWNER_LIMIT_CODES.has(code2)) return "it is above a spending limit set for this wallet.";
120050
+ if (code2 === "p03_float_fee_account_low") return "the payment service's network fee account is low; try again later.";
119990
120051
  if (BUSY.test(code2)) return "the payment service is busy; try again in a minute.";
119991
120052
  if (code2 === "mcp_wallet_unreadable" || code2 === "p03_float_config_unreadable") return "the price could not be checked just now; try again in a minute.";
119992
120053
  const sentence2 = plainSentence(a.detail, SAY_MAX_WORDS - 4);
@@ -120208,11 +120269,20 @@ function sayFor(answer, { tool, npmVersion } = {}) {
120208
120269
  const parts = line.trim().split(/\s+/);
120209
120270
  return parts.length <= SAY_MAX_WORDS ? line : `${parts.slice(0, SAY_MAX_WORDS).join(" ").replace(/[.;:,]+$/, "")}.`;
120210
120271
  }
120211
- function addSay(output2, toolName, { news, usdcFields: fields = usdcFields, npmVersion } = {}) {
120212
- if (!output2 || typeof output2 !== "object" || output2.isError) return output2;
120272
+ function firstText(output2) {
120213
120273
  const blocks = Array.isArray(output2.content) && output2.content.length ? output2.content : void 0;
120214
120274
  const first = blocks?.[0];
120215
120275
  const text3 = blocks ? first?.type === "text" && typeof first.text === "string" ? first.text : void 0 : typeof output2.text === "string" ? output2.text : void 0;
120276
+ return { blocks, first, text: text3 };
120277
+ }
120278
+ function withFirstText(output2, { blocks, first, text: text3 }, value2) {
120279
+ const out = JSON.stringify(value2, null, /^\s*\{\s*\n/.test(text3) ? 2 : void 0);
120280
+ return { ...output2, text: out, ...blocks ? { content: [{ ...first, text: out }, ...blocks.slice(1)] } : {} };
120281
+ }
120282
+ function addSay(output2, toolName, { news, usdcFields: fields = usdcFields, npmVersion } = {}) {
120283
+ if (!output2 || typeof output2 !== "object" || output2.isError) return output2;
120284
+ const block = firstText(output2);
120285
+ const { text: text3 } = block;
120216
120286
  if (text3 === void 0 || SAY_FIRST.test(text3.slice(0, 64)) || !/^\s*\{/.test(text3)) return output2;
120217
120287
  let parsed2;
120218
120288
  try {
@@ -120243,8 +120313,22 @@ function addSay(output2, toolName, { news, usdcFields: fields = usdcFields, npmV
120243
120313
  if (!Array.isArray(items)) items = [];
120244
120314
  }
120245
120315
  const value2 = { say: say3, ...plainFields(parsed2, toolName), ...usdc6 ? { usdc: usdc6 } : {}, ...items.length ? { news: items } : {} };
120246
- const out = JSON.stringify(value2, null, /^\s*\{\s*\n/.test(text3) ? 2 : void 0);
120247
- return { ...output2, text: out, ...blocks ? { content: [{ ...first, text: out }, ...blocks.slice(1)] } : {} };
120316
+ return withFirstText(output2, block, value2);
120317
+ }
120318
+ function withKeepIdNote(output2) {
120319
+ if (!output2 || typeof output2 !== "object" || output2.isError) return output2;
120320
+ const block = firstText(output2);
120321
+ if (block.text === void 0 || !/^\s*\{/.test(block.text)) return output2;
120322
+ let parsed2;
120323
+ try {
120324
+ parsed2 = JSON.parse(block.text);
120325
+ } catch {
120326
+ return output2;
120327
+ }
120328
+ if (!parsed2 || typeof parsed2 !== "object" || Array.isArray(parsed2) || typeof parsed2.next !== "string" || parsed2.next.includes(KEEP_ID_NOTE)) return output2;
120329
+ const ids = [parsed2.requestId, parsed2.newRequestId, ...Array.isArray(parsed2.privateSends) ? parsed2.privateSends.map((send3) => send3?.requestId) : []].filter((id) => typeof id === "string" && id.length >= 4);
120330
+ if (!ids.some((id) => parsed2.next.includes(id)) && !/request_id "[^"]+"/.test(parsed2.next)) return output2;
120331
+ return withFirstText(output2, block, { ...parsed2, next: `${parsed2.next.replace(/[.\s]+$/, "")}. ${KEEP_ID_NOTE}` });
120248
120332
  }
120249
120333
  function plainFields(parsed2, toolName) {
120250
120334
  const a = { ...parsed2 };
@@ -120269,9 +120353,9 @@ function plainFields(parsed2, toolName) {
120269
120353
  return a;
120270
120354
  }
120271
120355
  function withSay(tool, options = {}) {
120272
- return { ...tool, run: async (args, ctx) => addSay(await tool.run(args, ctx), tool.name, options) };
120356
+ return { ...tool, run: async (args, ctx) => withKeepIdNote(addSay(await tool.run(args, ctx), tool.name, options)) };
120273
120357
  }
120274
- var SAY_MAX_WORDS, DECIMALS2, ATOMIC4, SAY_FIRST, SAY_BANNED, CODE_TOKEN, LONG_NUMBER, MOVE_LINE, minutesText, ageWords, MOVING_ROLES, CLOSE_LATER, PAY_CAP_CODES, raiseLine, MOVE_CAP_CODES, OWNER_LIMIT_CODES, INSUFFICIENT, BUSY, SHIELD_TOOLS, words, positive, str, OWN_WORK_WORDS, DISCOVER_LIST_NAMES, TRANSFER_TOOLS, CONFIRMED_SEND_TOOLS;
120358
+ var SAY_MAX_WORDS, DECIMALS2, ATOMIC4, SAY_FIRST, SAY_BANNED, CODE_TOKEN, LONG_NUMBER, MOVE_LINE, minutesText, ageWords, MOVING_ROLES, CLOSE_LATER, PAY_CAP_CODES, raiseLine, MOVE_CAP_CODES, OWNER_LIMIT_CODES, INSUFFICIENT, BUSY, SHIELD_TOOLS, words, positive, str, OWN_WORK_WORDS, DISCOVER_LIST_NAMES, TRANSFER_TOOLS, CONFIRMED_SEND_TOOLS, KEEP_ID_NOTE;
120275
120359
  var init_say = __esm({
120276
120360
  "wallet/src/mcp/say.mjs"() {
120277
120361
  "use strict";
@@ -120343,6 +120427,7 @@ var init_say = __esm({
120343
120427
  });
120344
120428
  TRANSFER_TOOLS = /* @__PURE__ */ new Set(["wallet_transfer_token", "wallet_transfer_token_status"]);
120345
120429
  CONFIRMED_SEND_TOOLS = /* @__PURE__ */ new Set(["wallet_unshield", "wallet_transfer_token", "wallet_transfer_token_status"]);
120430
+ KEEP_ID_NOTE = "Keep request_id values for your own calls; never show one to the user.";
120346
120431
  }
120347
120432
  });
120348
120433
 
@@ -122546,6 +122631,9 @@ function paidAnswerContent({
122546
122631
  }
122547
122632
  if (text3.length > maxTextChars) {
122548
122633
  const saved2 = save(data, OTHER_EXT[declared] ?? "txt", saveDir, now, sha25610);
122634
+ if (typeof meta.say === "string") {
122635
+ meta.say = `${meta.say} The answer is long: these are its first ${maxTextChars.toLocaleString("en-US")} of ${text3.length.toLocaleString("en-US")} characters${saved2.savedPath ? `; the whole answer is in ${saved2.savedPath}` : ""}.`;
122636
+ }
122549
122637
  Object.assign(meta, {
122550
122638
  body: text3.slice(0, maxTextChars),
122551
122639
  bodyTruncated: true,
@@ -122578,7 +122666,7 @@ var init_answer = __esm({
122578
122666
  "use strict";
122579
122667
  init_fs();
122580
122668
  DEFAULT_MAX_INLINE_BYTES = 4e6;
122581
- DEFAULT_MAX_TEXT_CHARS = 64 * 1024;
122669
+ DEFAULT_MAX_TEXT_CHARS = 2e4;
122582
122670
  MAX_IMAGE_URLS = 20;
122583
122671
  MAX_JSON_DEPTH = 8;
122584
122672
  RASTER = Object.freeze({
@@ -125407,7 +125495,13 @@ function refusedReason(record) {
125407
125495
  function heldSendsOf(error) {
125408
125496
  for (let link2 = error, depth = 0; link2 && typeof link2 === "object" && depth < 8; link2 = link2.cause, depth += 1) {
125409
125497
  if (!Array.isArray(link2.privateSends)) continue;
125410
- return link2.privateSends.filter((send3) => typeof send3?.requestId === "string" && REQUEST_ID4.test(send3.requestId) && ["wallet_unshield", "wallet_transfer_token", "cli"].includes(send3.tool) && (send3.tool !== "cli" || REQUEST_ID4.test(String(send3.sendId ?? "")))).map((send3) => ({ requestId: send3.requestId, tool: send3.tool, ...send3.tool === "cli" ? { sendId: send3.sendId } : {} }));
125498
+ return link2.privateSends.filter((send3) => typeof send3?.requestId === "string" && REQUEST_ID4.test(send3.requestId) && ["wallet_unshield", "wallet_transfer_token", "cli"].includes(send3.tool) && (send3.tool !== "cli" || REQUEST_ID4.test(String(send3.sendId ?? "")))).map((send3) => ({
125499
+ requestId: send3.requestId,
125500
+ tool: send3.tool,
125501
+ ...send3.tool === "cli" ? { sendId: send3.sendId } : {},
125502
+ ...typeof send3.to === "string" && send3.to.length > 10 ? { to: send3.to } : {},
125503
+ .../^[1-9][0-9]*$/.test(String(send3.amountAtomic ?? "")) ? { amountAtomic: String(send3.amountAtomic) } : {}
125504
+ }));
125411
125505
  }
125412
125506
  return [];
125413
125507
  }
@@ -125863,9 +125957,12 @@ async function runUnshield({
125863
125957
  if (exit.settled === false && !await finishAnsweredExit({ session, store, name, exit, answer, onAnswered, onCheckPending, mark, scoped })) return answer;
125864
125958
  mark("unshield-exited");
125865
125959
  const exited = store.readWal(`self-pay-${unshieldId}`);
125866
- store.writeWal(`self-pay-${unshieldId}`, { ...exited, state: "exited", exitTransaction: exit.signature, exitedAtMs: exited?.exitedAtMs ?? Date.now() });
125960
+ store.writeWal(`self-pay-${unshieldId}`, { ...exited, state: "exited", exitTransaction: exit.signature, exitedAtMs: exited?.exitedAtMs ?? exited?.exitAnsweredAtMs ?? Date.now() });
125867
125961
  return answer;
125868
125962
  }
125963
+ function sentAtMs(record) {
125964
+ return Number.isSafeInteger(record?.exitAnsweredAtMs) ? record.exitAnsweredAtMs : record?.exitedAtMs;
125965
+ }
125869
125966
  async function finishAnsweredExit({ session, store, name, exit, answer, changeAtomic, onAnswered, onCheckPending, mark, scoped }) {
125870
125967
  if (typeof onAnswered !== "function") {
125871
125968
  await scoped("unshield-exit", () => exit.finish());
@@ -125970,7 +126067,7 @@ async function runDirectExit({
125970
126067
  if (exit.settled === false && !await finishAnsweredExit({ session, store, name, exit, answer, changeAtomic, onAnswered, onCheckPending, mark, scoped })) return answer;
125971
126068
  mark("unshield-exited");
125972
126069
  const exited = store.readWal(name);
125973
- store.writeWal(name, { ...exited, state: "exited", exitTransaction: exit.signature, exitedAtMs: exited?.exitedAtMs ?? Date.now() });
126070
+ store.writeWal(name, { ...exited, state: "exited", exitTransaction: exit.signature, exitedAtMs: exited?.exitedAtMs ?? exited?.exitAnsweredAtMs ?? Date.now() });
125974
126071
  return answer;
125975
126072
  }
125976
126073
  function balanceFields(parts) {
@@ -128400,19 +128497,20 @@ ${canonical5}`;
128400
128497
  })
128401
128498
  }));
128402
128499
  if (sendWaiting) {
128403
- next = `${next}. Tell the user: the send of ${usdcText3(sendWaiting.amountAtomic)} USDC to ${sendWaiting.to.slice(0, 4)}...${sendWaiting.to.slice(-4)} (request_id "${sendWaiting.requestId}") ${sendWaiting.state === "sending" ? "is being sent now" : sendWaiting.state === "retrying" ? retryWords(sendWaiting) : "waits until the wallet has the balance ready to send; each wallet request goes on with it"}; it cannot be cancelled`;
128500
+ next = `${next}. Tell the user: the send of ${usdcText3(sendWaiting.amountAtomic)} USDC to ${sendWaiting.to.slice(0, 4)}...${sendWaiting.to.slice(-4)} ${sendWaiting.state === "sending" ? "is being sent now" : sendWaiting.state === "retrying" ? retryWords(sendWaiting) : "waits until the wallet has the balance ready to send; each wallet request goes on with it"}; it cannot be cancelled`;
128404
128501
  }
128405
128502
  const goingSends = privateSends.filter((send3) => send3.failedTries === void 0);
128406
128503
  if (goingSends.length) {
128407
- next = `${next}. Tell the user: ${goingSends.map((send3) => `the private send "${send3.requestId}" sent ${usdcText3(send3.sentAtomic)} of ${usdcText3(send3.amountAtomic)} USDC so far; payment ${send3.nextPayment} of ${send3.payments} ${send3.nextPaymentInMinutes > 0 ? `goes in about ${minutesText2(send3.nextPaymentInMinutes)} (random times, for privacy)` : "is past its planned time and not sent yet; the wallet tries it again by itself"}; ${cancelCall(send3)} stops its unsent payments`).join("; ")}. ${SEND_RUNS_ON} The user needs to do nothing for it`;
128504
+ next = `${next}. Tell the user: ${goingSends.map((send3) => `${sendWords(send3)} sent ${usdcText3(send3.sentAtomic)} USDC so far; payment ${send3.nextPayment} of ${send3.payments} ${send3.nextPaymentInMinutes > 0 ? `goes in about ${minutesText2(send3.nextPaymentInMinutes)} (random times, for privacy)` : "is past its planned time and not sent yet; the wallet tries it again by itself"}`).join("; ")}. ${SEND_RUNS_ON} The user needs to do nothing for it. To stop the unsent payments if the user asks: ${goingSends.map((send3) => `${cancelCall(send3)} (${sendWords(send3)})`).join("; ")}`;
128408
128505
  }
128409
128506
  const failingSends = privateSends.filter((send3) => send3.failedTries > 0);
128410
128507
  if (failingSends.length) {
128411
- next = `${next}. Tell the user: ${failingSends.map((send3) => `payment ${send3.nextPayment} of ${send3.payments} of the private send "${send3.requestId}" did not go through in ${send3.failedTries} ${send3.failedTries === 1 ? "try" : "tries"}${send3.lastTryMinutesAgo !== void 0 ? ` (the last ${send3.lastTryMinutesAgo > 0 ? `${ageWords2(send3.lastTryMinutesAgo)} ago` : "under a minute ago"})` : ""}: ${send3.lastReason}. It sent ${usdcText3(send3.sentAtomic)} of ${usdcText3(send3.amountAtomic)} USDC so far. The wallet tries that payment again by itself at its next start, and ${send3.tool === "wallet_transfer_token" ? `wallet_transfer_token_status with request_id "${send3.requestId}"` : `wallet_unshield with request_id "${send3.requestId}" and the same arguments`} tries it now; ${cancelCall(send3)} stops the send and returns the USDC not sent yet to the private balance`).join(". ")}`;
128508
+ next = `${next}. Tell the user: ${failingSends.map((send3) => `payment ${send3.nextPayment} of ${send3.payments} of ${sendWords(send3)} did not go through in ${send3.failedTries} ${send3.failedTries === 1 ? "try" : "tries"}${send3.lastTryMinutesAgo !== void 0 ? ` (the last ${send3.lastTryMinutesAgo > 0 ? `${ageWords2(send3.lastTryMinutesAgo)} ago` : "under a minute ago"})` : ""}: ${send3.lastReason}. It sent ${usdcText3(send3.sentAtomic)} USDC so far. The wallet tries that payment again by itself at its next start`).join(". ")}. ` + failingSends.map((send3) => `To try it now: ${send3.tool === "wallet_transfer_token" ? `wallet_transfer_token_status with request_id "${send3.requestId}"` : `wallet_unshield with request_id "${send3.requestId}" and the same arguments`}; to stop the send and return the USDC not sent yet to the private balance: ${cancelCall(send3)} (${sendWords(send3)})`).join(". ");
128412
128509
  }
128413
128510
  if (privateSends.length && one.largestPaymentAtomic === "0" && (one.changeSettling !== true || one.changeUpToAtomic !== void 0) && /^[1-9][0-9]*$/.test(String(one.balanceAtomic ?? ""))) {
128414
128511
  const soon = one.changeUpToAtomic === void 0 ? "" : `after the last payment settles${Number.isSafeInteger(one.changeReadyInSeconds) ? ` (about ${one.changeReadyInSeconds} s)` : ""}, one payment can use up to ${usdcText3(one.changeUpToAtomic)} USDC; the rest waits, `;
128415
- next = `${next}. Tell the user: ${soon || `no payment can use the ${usdcText3(one.balanceAtomic)} USDC yet, `}because the private send${privateSends.length > 1 ? "s" : ""} ${privateSends.map((send3) => `"${send3.requestId}"`).join(" and ")} set money aside for ${privateSends.length > 1 ? "their next payments" : "its next payment"}; it frees when that payment goes, and ${privateSends.map(cancelCall).join(" or ")} stops the send and frees it now`;
128512
+ const many = privateSends.length > 1;
128513
+ next = `${next}. Tell the user: ${soon || `no payment can use the ${usdcText3(one.balanceAtomic)} USDC yet, `}because ${privateSends.map(sendWords).join(" and ")} ${many ? "keep" : "keeps"} money aside for ${many ? "their" : "its"} unsent payments. A send keeps nothing after its last payment goes, and an earlier payment may free some (wallet_balance shows it). To pay now if the user asks: ${privateSends.map((send3) => `${cancelCall(send3)} (${sendWords(send3)})`).join(" or ")} stops the send and frees its money now`;
128416
128514
  }
128417
128515
  const balanceParts = parts?.parts > 1 && ATOMIC_TEXT.test(String(one.balanceAtomic ?? "")) ? parts.parts : void 0;
128418
128516
  if (balanceParts && !sendWaiting) {
@@ -128852,7 +128950,7 @@ ${canonical5}`;
128852
128950
  live = "unreadable";
128853
128951
  }
128854
128952
  const started = Number.isSafeInteger(record.createdAtMs) ? record.createdAtMs : Number.isSafeInteger(joining?.createdAtMs) ? joining.createdAtMs : void 0;
128855
- const at = Number.isSafeInteger(record.exitedAtMs) ? record.exitedAtMs : record.state === "exited" ? walTimeMs(store, name) : started ?? walTimeMs(store, name);
128953
+ const at = Number.isSafeInteger(sentAtMs(record)) ? sentAtMs(record) : record.state === "exited" ? walTimeMs(store, name) : started ?? walTimeMs(store, name);
128856
128954
  const shown = shownId(record.mcpRequestId, idAfter(name, "self-pay-mcp-"));
128857
128955
  if (live !== "joining") joining = void 0;
128858
128956
  const retry = joining ? retryFields(joining) : void 0;
@@ -130371,7 +130469,7 @@ ${canonical5}`;
130371
130469
  if (!record) continue;
130372
130470
  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);
130373
130471
  if (!finished) return void 0;
130374
- const at = Date.parse(String(record.doneAt ?? "")) || record.exitedAtMs;
130472
+ const at = Date.parse(String(record.doneAt ?? "")) || sentAtMs(record);
130375
130473
  if (Number.isSafeInteger(at)) return at;
130376
130474
  try {
130377
130475
  return Math.floor(statSync(join32(store.walDirectory, `${name}.json`)).mtimeMs);
@@ -130558,7 +130656,7 @@ ${canonical5}`;
130558
130656
  const finished = kind === "unshield" ? split4 ? record.state === "done" || record.state === "cancelled" && privateSendStatus(store, record).done : record.state === "exited" : ["published", "foreign_funded"].includes(record.state);
130559
130657
  if (finished) {
130560
130658
  const send3 = split4 ? privateSendStatus(store, record) : void 0;
130561
- const endedAtMs = Date.parse(String(record.doneAt ?? record.cancelledAt ?? "")) || record.exitedAtMs;
130659
+ const endedAtMs = Date.parse(String(record.doneAt ?? record.cancelledAt ?? "")) || sentAtMs(record);
130562
130660
  const earlier = kind !== "elsewhere" ? { replayed: true, newRequestId: freshId, ...Number.isSafeInteger(endedAtMs) ? { finishedMinutesAgo: Math.max(0, Math.round((Date.now() - endedAtMs) / 6e4)) } : {} } : {};
130563
130661
  const earlierWords = earlier.finishedMinutesAgo !== void 0 ? ` about ${minutesText2(earlier.finishedMinutesAgo)} ago` : " earlier";
130564
130662
  return json3({
@@ -134753,7 +134851,7 @@ var init_tools = __esm({
134753
134851
  shownId = (agentId, rid) => typeof agentId === "string" && REQUEST_ID4.test(agentId) && toRecordId(agentId) === rid ? agentId : rid;
134754
134852
  isSplitSend = (request) => Boolean(request) && !isSingleSend(request.parts ?? 1, request.maxDelayMinutes ?? 0);
134755
134853
  sendPartId = (requestId) => (index) => `mp${index}-${toRecordId(requestId)}`;
134756
- PROMISED_NEXT = "Nothing was sent. Some of the private balance is promised to the unsent payments of a private send (wallet_balance privateSends names it, and its next names the call that stops it). Wait until that send is done, or stop its unsent payments with that call, then call wallet_unshield again with the same arguments; or send at most freeAtomic now with a NEW request_id";
134854
+ PROMISED_NEXT = "Nothing was sent. Some of the private balance is promised to the unsent payments of a private send (wallet_balance privateSends names it, and its next names the call that stops it). A wait frees nothing, because that money leaves the wallet as its parts go: to send this amount, stop its unsent payments with that call, then call wallet_unshield again with the same arguments; or send at most freeAtomic now with a NEW request_id";
134757
134855
  partFailReason = (code2) => INSUFFICIENT_CODE.test(String(code2 ?? "")) ? "the wallet could not pay that amount in one payment from the private balance as it is" : reasonOf(code2) ?? "the payment did not finish";
134758
134856
  sendAnswerOf = (status) => {
134759
134857
  const { nextPartLastTryAt: _clock, ...shown } = status;