shinjuku-shield 0.3.111 → 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.111. 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.111
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.111 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.111 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.111", "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": "ca9504f8356a05d2a1fc36428f6443419e6943406d11e201d4fa2ff9d6c84814",
5
- "bytes": 8333810,
6
- "sourceCommit": "b7bf7b1d3617d779db6012f60c35e34f7e8363da",
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-27T14:51:57.097Z"
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.111",
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": "ca9504f8",
7
- "shinjukuSha256": "ca9504f8356a05d2a1fc36428f6443419e6943406d11e201d4fa2ff9d6c84814",
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
  }
@@ -119435,16 +119494,15 @@ function plainSentence(next, room) {
119435
119494
  }
119436
119495
  return "";
119437
119496
  }
119438
- function heldBySendSay(sends, verb) {
119439
- const send3 = Array.isArray(sends) ? sends.find((one) => typeof one?.requestId === "string") : void 0;
119440
- return send3 ? `Nothing was ${verb}. Your private send ${send3.requestId} set this money aside for its next payment; stop that send to pay now, or add money.` : `Nothing was ${verb}. A private send in progress set this money aside. Stop that send to pay now, or add money.`;
119497
+ function heldBySendSay(verb) {
119498
+ return `Nothing was ${verb}. A private send in progress set this money aside. Stop that send to pay now, or add money.`;
119441
119499
  }
119442
119500
  function refusalSay(a, tool, again, npmVersion) {
119443
119501
  const code2 = str(a.error);
119444
119502
  const detail = str(a.detail);
119445
119503
  const next = str(a.next);
119446
119504
  const moneyVerb = MOVE_CAP_CODES.has(code2) || tool === "wallet_shield" || tool === "wallet_unshield" ? "moved" : "paid";
119447
- const ask = typeof a.requestId === "string" && a.requestId ? `Ask again with request_id ${a.requestId}` : "Ask again with the same request_id";
119505
+ const ask = "Ask again";
119448
119506
  if (/_refund_pending$/.test(code2)) {
119449
119507
  const total2 = usdcText2(a.refundAtomic ?? a.paidAtomic);
119450
119508
  const when = Number.isSafeInteger(a.refundEtaSeconds) && a.refundEtaSeconds > 0 ? ` within about ${minutesText(Math.ceil(a.refundEtaSeconds / 60))}` : "";
@@ -119461,14 +119519,13 @@ function refusalSay(a, tool, again, npmVersion) {
119461
119519
  if (a.refundArrived === true) return `The seller did not take it. ${yours} is back in your private balance. ${other}`;
119462
119520
  return `The seller did not take it; ${total2 ? `${total2} USDC` : "your money"} comes back by itself${when}. ${other}`;
119463
119521
  }
119464
- const askAgain = typeof a.newRequestId === "string" && a.newRequestId ? `To ask again, use request_id ${a.newRequestId}; it pays again.` : "A new request_id pays again.";
119522
+ const askAgain = "Asking again pays again.";
119465
119523
  if (code2 === "p03_float_receipt_pruned" && a.conversationNotKept === true) {
119466
119524
  return `Paid once, not again: the wallet keeps no copy of a private conversation. ${askAgain}`;
119467
119525
  }
119468
119526
  if (code2 === "p03_float_receipt_pruned") return `This request was paid more than 10 minutes ago. ${askAgain}`;
119469
119527
  if (/^p03_(float|resource_payment)_request_(id_conflict|data_mismatch)$/.test(code2)) {
119470
- const fresh = typeof a.newRequestId === "string" && a.newRequestId ? `request_id ${a.newRequestId}` : "a new request_id";
119471
- return `Nothing was paid: this request_id belongs to an earlier payment. For this request, ask again with ${fresh}.`;
119528
+ return "Nothing was paid: this request matched an earlier payment with other details. Ask again to pay for this one.";
119472
119529
  }
119473
119530
  if (code2 === "p03_resource_payment_memory_only_needs_float") {
119474
119531
  return "Nothing was paid: this seller now asks a payment whose record would keep your words, and a private call never pays that way. Ask again.";
@@ -119530,6 +119587,7 @@ function refusalSay(a, tool, again, npmVersion) {
119530
119587
  if (/_retry_same_request$/.test(code2)) {
119531
119588
  return a.paid === true ? `Paid, but the seller has not answered yet. ${again}` : `The result is not known yet. ${again}`;
119532
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.";
119533
119591
  if (BUSY.test(code2)) {
119534
119592
  const said = /in about ([0-9]{1,4}) s\b/.exec(next);
119535
119593
  const seconds2 = Number.isFinite(a.readyInSeconds) && a.readyInSeconds > 0 ? Math.ceil(a.readyInSeconds) : said ? Number(said[1]) : 30;
@@ -119568,7 +119626,7 @@ function refusalSay(a, tool, again, npmVersion) {
119568
119626
  if (code2 === "mcp_setup_restart_needed") return "The wallet is set up, but it must reconnect once. Follow the steps below, then try again. Nothing was paid.";
119569
119627
  if (code2 === "mcp_restart_needed") return "Nothing was paid. Restart this app, then try again.";
119570
119628
  if (code2 === "p03_merchant_respend_value_held_by_private_send") {
119571
- return heldBySendSay(a.privateSends, tool === "wallet_unshield" || tool === "wallet_transfer_token" || tool === "wallet_transfer_token_status" ? "sent" : "paid");
119629
+ return heldBySendSay(tool === "wallet_unshield" || tool === "wallet_transfer_token" || tool === "wallet_transfer_token_status" ? "sent" : "paid");
119572
119630
  }
119573
119631
  if (/accounts_split/.test(code2) || /accounts_split/.test(detail) || typeof a.joining === "boolean") {
119574
119632
  const verb = tool === "wallet_unshield" ? "sent" : "paid";
@@ -119837,8 +119895,7 @@ function heldByPaymentSay(detail) {
119837
119895
  if (!items.length) return "";
119838
119896
  const held = usdcText2(detail.unspentAtomic);
119839
119897
  const what = positive(held) ? `${held} USDC is` : "Your USDC is";
119840
- const id = items.find((item) => typeof item.requestId === "string" && item.requestId)?.requestId;
119841
- return id ? `${what} held by an unfinished payment (request_id ${id}). Ask me to finish it, or to close it with wallet_cancel.` : `${what} held by an unfinished payment. Ask me to finish it, or to close it with wallet_cancel.`;
119898
+ return `${what} held by an unfinished payment. Ask me to finish it, or to close it with wallet_cancel.`;
119842
119899
  }
119843
119900
  function ownWorkWords(lockedBy) {
119844
119901
  return typeof lockedBy === "string" && Object.hasOwn(OWN_WORK_WORDS, lockedBy) ? OWN_WORK_WORDS[lockedBy] : void 0;
@@ -119914,25 +119971,29 @@ function balanceLine(a, room = SAY_MAX_WORDS) {
119914
119971
  const going = sends.find((send3) => Number.isSafeInteger(send3.failedTries) && send3.failedTries > 0) ?? sends[0];
119915
119972
  if (going && !moving) {
119916
119973
  const left = usdcText2(going.leftAtomic) ?? "";
119974
+ const total = usdcText2(going.amountAtomic);
119975
+ const to = typeof going.to === "string" && going.to.length > 10 ? ` to ${going.to.slice(0, 4)}...${going.to.slice(-4)}` : "";
119976
+ const named = `your private send${total ? ` of ${total} USDC` : ""}${to}`;
119977
+ const short = `your private send${to}`;
119917
119978
  if (Number.isSafeInteger(going.failedTries) && going.failedTries > 0) {
119918
119979
  const tries = `${going.failedTries} ${going.failedTries === 1 ? "try" : "tries"}`;
119919
119980
  return have(
119920
- `; your private send "${going.requestId}" has ${left} USDC not sent: its next payment did not go through (${tries}).`,
119921
- `; private send "${going.requestId}": next payment did not go through (${tries}).`,
119981
+ `; ${named} has ${left} USDC not sent: its next payment did not go through (${tries}).`,
119982
+ `; ${short}: next payment did not go through (${tries}).`,
119922
119983
  "; a private send's next payment did not go through yet."
119923
119984
  );
119924
119985
  }
119925
119986
  if (!(Number.isSafeInteger(going.nextPaymentInMinutes) && going.nextPaymentInMinutes > 0)) {
119926
119987
  return have(
119927
- `; your private send "${going.requestId}" has ${left} USDC not sent yet, and the wallet tries its next payment again by itself.`,
119928
- `; private send "${going.requestId}": ${left} USDC not sent yet.`,
119988
+ `; ${named} has ${left} USDC not sent yet, and the wallet tries its next payment again by itself.`,
119989
+ `; ${short}: ${left} USDC not sent yet.`,
119929
119990
  "; a private send is not all sent yet."
119930
119991
  );
119931
119992
  }
119932
119993
  const when = `in about ${minutesText(going.nextPaymentInMinutes)}`;
119933
119994
  return have(
119934
- `; your private send "${going.requestId}" has ${left} USDC left to go, and its next payment goes ${when}.`,
119935
- `; private send "${going.requestId}": next payment ${when}.`,
119995
+ `; ${named} has ${left} USDC left to go, and its next payment goes ${when}.`,
119996
+ `; ${short}: next payment ${when}.`,
119936
119997
  `; a private send's next payment goes ${when}.`
119937
119998
  );
119938
119999
  }
@@ -119986,6 +120047,7 @@ function previewReason(a, npmVersion) {
119986
120047
  return raiseLine("--max-session", npmVersion, `over your 24-hour limit of ${usdcText2(a.capAtomic)} USDC.`, SAY_MAX_WORDS - 3);
119987
120048
  }
119988
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.";
119989
120051
  if (BUSY.test(code2)) return "the payment service is busy; try again in a minute.";
119990
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.";
119991
120053
  const sentence2 = plainSentence(a.detail, SAY_MAX_WORDS - 4);
@@ -120034,8 +120096,7 @@ function transferSay(a) {
120034
120096
  const kept2 = a.send?.keptPrivateAtomic && a.send.keptPrivateAtomic !== "0" ? usdcText2(a.send.keptPrivateAtomic) : null;
120035
120097
  const later = "If this chat ends first, it continues at your next request.";
120036
120098
  if (a.replayed === true) {
120037
- const fresh = typeof a.newRequestId === "string" && a.newRequestId ? `request_id ${a.newRequestId}` : "a new request_id";
120038
- return `This request_id belongs to an earlier transfer to ${to}; this call moved nothing. To transfer again, ask with ${fresh}.`;
120099
+ return `This request matched an earlier transfer to ${to}; this call moved nothing. Ask again to transfer again.`;
120039
120100
  }
120040
120101
  switch (a.stage) {
120041
120102
  case "selling":
@@ -120046,7 +120107,7 @@ function transferSay(a) {
120046
120107
  // At most 24 words with the kept part (SAY_MAX_WORDS 25). A send step that stopped (tools.mjs transferAnswer sendStop,
120047
120108
  // 0.3.109 fault 5) says why first: money another send set aside, or money the wallet gets ready by itself.
120048
120109
  case "sending":
120049
- if (Array.isArray(a.privateSends)) return heldBySendSay(a.privateSends, "sent");
120110
+ if (Array.isArray(a.privateSends)) return heldBySendSay("sent");
120050
120111
  if (a.joining === true) return "Not sent yet: the wallet gets the money ready by itself. Ask again in a few minutes; do not cancel.";
120051
120112
  return sent ? `Sending ${sent} USDC (not the token) privately to ${to}${kept2 ? `; ${kept2} USDC stays private` : ""}. ${later}` : `Sending ${usdc6} privately to ${to}. It gets USDC, not the token. ${later}`;
120052
120113
  case "sold_waiting_room":
@@ -120089,8 +120150,7 @@ function stoppedSay(a) {
120089
120150
  }
120090
120151
  function build(a, tool, npmVersion) {
120091
120152
  if (!a || typeof a !== "object" || Array.isArray(a)) return void 0;
120092
- const id = typeof a.requestId === "string" && a.requestId ? a.requestId : void 0;
120093
- const again = id ? `Ask again with request_id ${id}; it never pays twice.` : "Ask again with the same request_id; it never pays twice.";
120153
+ const again = "Ask again; it never pays twice.";
120094
120154
  if (a.ok === false) return refusalSay(a, tool, again, npmVersion);
120095
120155
  if (TRANSFER_TOOLS.has(tool) || tool === void 0 && a.delivers === "USDC" && (a.token || Array.isArray(a.transfers))) return transferSay(a);
120096
120156
  if (tool === "wallet_receive_shielded" || tool === void 0 && typeof a.receiveShielded === "string") {
@@ -120133,7 +120193,7 @@ function build(a, tool, npmVersion) {
120133
120193
  return `${what} The wallet first gets your whole balance ready to send${steps}, then sends it.`;
120134
120194
  }
120135
120195
  const head = `Not sent yet: the wallet is getting your balance ready to send${steps}.`;
120136
- const ask = id ? `Ask again with request_id ${id} until it sends` : "Ask again with the same request_id until it sends";
120196
+ const ask = "Ask again until it sends";
120137
120197
  return [`${head} ${ask}; it never sends twice.`, `${head} ${ask}.`].find((line) => words(line) <= SAY_MAX_WORDS) ?? head;
120138
120198
  }
120139
120199
  if (typeof a.to === "string" && a.to && Object.hasOwn(a, "stage")) {
@@ -120168,15 +120228,14 @@ function build(a, tool, npmVersion) {
120168
120228
  if (typeof a.address === "string" && a.address && typeof a.stage === "string") return linkSay(a);
120169
120229
  if (a.stage === "waiting_for_funds" && typeof a.depositAddress === "string") {
120170
120230
  const send3 = usdcText2(a.sendAtomic ?? a.amountAtomic);
120171
- return `Send exactly ${send3 ? `${send3} USDC` : "the amount"} on Solana to ${a.depositAddress} in one transfer. Then ask again with request_id ${id ?? "the same request_id"}.`;
120231
+ return `Send exactly ${send3 ? `${send3} USDC` : "the amount"} on Solana to ${a.depositAddress} in one transfer. Then ask again.`;
120172
120232
  }
120173
120233
  const shieldJob = SHIELD_TOOLS.has(tool) || tool === void 0 && Object.hasOwn(a, "amountAtomic") && Object.hasOwn(a, "stage") && !Object.hasOwn(a, "paid");
120174
120234
  if (shieldJob && typeof a.stage === "string") {
120175
120235
  const amount = usdcText2(a.amountAtomic);
120176
120236
  const what = amount ? `${amount} USDC` : "The USDC";
120177
120237
  if (a.done === true && a.replayed === true) {
120178
- const fresh = typeof a.newRequestId === "string" && a.newRequestId ? `request_id ${a.newRequestId}` : "a new request_id";
120179
- return `This request_id finished earlier (${what} made private then); this call moved nothing. To shield more, ask again with ${fresh}.`;
120238
+ return `This request finished earlier (${what} made private then); this call moved nothing. Ask again to shield more.`;
120180
120239
  }
120181
120240
  if (a.done === true) return withBalance(`${what} is now private.`, a);
120182
120241
  if (a.stage === "waiting" && a.code === "mcp_balance_unknown") {
@@ -120189,14 +120248,14 @@ function build(a, tool, npmVersion) {
120189
120248
  }
120190
120249
  if (tool === "wallet_receipts" && Number.isSafeInteger(a.count)) {
120191
120250
  const arrived = Array.isArray(a.receipts) ? a.receipts.find((row) => row?.kind === "add-funds" && /^[1-9][0-9]*$/.test(String(row.heldAtomic ?? ""))) : void 0;
120192
- if (arrived) return `${a.count} ${a.count === 1 ? "entry" : "entries"}. ${usdcText2(arrived.heldAtomic)} USDC reached add-funds address ${arrived.requestId} and becomes private by itself.`;
120251
+ if (arrived) return `${a.count} ${a.count === 1 ? "entry" : "entries"}. ${usdcText2(arrived.heldAtomic)} USDC reached an add-funds address and becomes private by itself.`;
120193
120252
  return a.count === 0 ? "No receipts or wallet moves yet." : `${a.count} ${a.count === 1 ? "entry" : "entries, newest first"}. Each payment names its seller; pay.shinjukustaition.com is Shinjuku, the private payment service.`;
120194
120253
  }
120195
120254
  if (Array.isArray(a.models) && a.models.length > 0 && a.paid === void 0) {
120196
120255
  const count2 = `${a.models.length} AI ${a.models.length === 1 ? "model is" : "models are"}`;
120197
120256
  return a.ready === false ? `Nothing was asked or paid. ${count2} listed, but asking one needs Tor on; the next step says how to turn it on.` : `Nothing was asked or paid. ${count2} ready: ask a question, and each answer is paid privately from your balance.`;
120198
120257
  }
120199
- if (a.done === false) return id ? `Still running. Ask again with request_id ${id}; it never moves money twice.` : "Still running. Ask again in about 30 seconds.";
120258
+ if (a.done === false) return "Still running. Ask again in about 30 seconds; it never moves money twice.";
120200
120259
  return "Done.";
120201
120260
  }
120202
120261
  function sayFor(answer, { tool, npmVersion } = {}) {
@@ -120210,11 +120269,20 @@ function sayFor(answer, { tool, npmVersion } = {}) {
120210
120269
  const parts = line.trim().split(/\s+/);
120211
120270
  return parts.length <= SAY_MAX_WORDS ? line : `${parts.slice(0, SAY_MAX_WORDS).join(" ").replace(/[.;:,]+$/, "")}.`;
120212
120271
  }
120213
- function addSay(output2, toolName, { news, usdcFields: fields = usdcFields, npmVersion } = {}) {
120214
- if (!output2 || typeof output2 !== "object" || output2.isError) return output2;
120272
+ function firstText(output2) {
120215
120273
  const blocks = Array.isArray(output2.content) && output2.content.length ? output2.content : void 0;
120216
120274
  const first = blocks?.[0];
120217
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;
120218
120286
  if (text3 === void 0 || SAY_FIRST.test(text3.slice(0, 64)) || !/^\s*\{/.test(text3)) return output2;
120219
120287
  let parsed2;
120220
120288
  try {
@@ -120245,8 +120313,22 @@ function addSay(output2, toolName, { news, usdcFields: fields = usdcFields, npmV
120245
120313
  if (!Array.isArray(items)) items = [];
120246
120314
  }
120247
120315
  const value2 = { say: say3, ...plainFields(parsed2, toolName), ...usdc6 ? { usdc: usdc6 } : {}, ...items.length ? { news: items } : {} };
120248
- const out = JSON.stringify(value2, null, /^\s*\{\s*\n/.test(text3) ? 2 : void 0);
120249
- 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}` });
120250
120332
  }
120251
120333
  function plainFields(parsed2, toolName) {
120252
120334
  const a = { ...parsed2 };
@@ -120271,9 +120353,9 @@ function plainFields(parsed2, toolName) {
120271
120353
  return a;
120272
120354
  }
120273
120355
  function withSay(tool, options = {}) {
120274
- 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)) };
120275
120357
  }
120276
- 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;
120277
120359
  var init_say = __esm({
120278
120360
  "wallet/src/mcp/say.mjs"() {
120279
120361
  "use strict";
@@ -120345,6 +120427,7 @@ var init_say = __esm({
120345
120427
  });
120346
120428
  TRANSFER_TOOLS = /* @__PURE__ */ new Set(["wallet_transfer_token", "wallet_transfer_token_status"]);
120347
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.";
120348
120431
  }
120349
120432
  });
120350
120433
 
@@ -122548,6 +122631,9 @@ function paidAnswerContent({
122548
122631
  }
122549
122632
  if (text3.length > maxTextChars) {
122550
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
+ }
122551
122637
  Object.assign(meta, {
122552
122638
  body: text3.slice(0, maxTextChars),
122553
122639
  bodyTruncated: true,
@@ -122580,7 +122666,7 @@ var init_answer = __esm({
122580
122666
  "use strict";
122581
122667
  init_fs();
122582
122668
  DEFAULT_MAX_INLINE_BYTES = 4e6;
122583
- DEFAULT_MAX_TEXT_CHARS = 64 * 1024;
122669
+ DEFAULT_MAX_TEXT_CHARS = 2e4;
122584
122670
  MAX_IMAGE_URLS = 20;
122585
122671
  MAX_JSON_DEPTH = 8;
122586
122672
  RASTER = Object.freeze({
@@ -125409,7 +125495,13 @@ function refusedReason(record) {
125409
125495
  function heldSendsOf(error) {
125410
125496
  for (let link2 = error, depth = 0; link2 && typeof link2 === "object" && depth < 8; link2 = link2.cause, depth += 1) {
125411
125497
  if (!Array.isArray(link2.privateSends)) continue;
125412
- 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
+ }));
125413
125505
  }
125414
125506
  return [];
125415
125507
  }
@@ -125865,9 +125957,12 @@ async function runUnshield({
125865
125957
  if (exit.settled === false && !await finishAnsweredExit({ session, store, name, exit, answer, onAnswered, onCheckPending, mark, scoped })) return answer;
125866
125958
  mark("unshield-exited");
125867
125959
  const exited = store.readWal(`self-pay-${unshieldId}`);
125868
- 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() });
125869
125961
  return answer;
125870
125962
  }
125963
+ function sentAtMs(record) {
125964
+ return Number.isSafeInteger(record?.exitAnsweredAtMs) ? record.exitAnsweredAtMs : record?.exitedAtMs;
125965
+ }
125871
125966
  async function finishAnsweredExit({ session, store, name, exit, answer, changeAtomic, onAnswered, onCheckPending, mark, scoped }) {
125872
125967
  if (typeof onAnswered !== "function") {
125873
125968
  await scoped("unshield-exit", () => exit.finish());
@@ -125972,7 +126067,7 @@ async function runDirectExit({
125972
126067
  if (exit.settled === false && !await finishAnsweredExit({ session, store, name, exit, answer, changeAtomic, onAnswered, onCheckPending, mark, scoped })) return answer;
125973
126068
  mark("unshield-exited");
125974
126069
  const exited = store.readWal(name);
125975
- 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() });
125976
126071
  return answer;
125977
126072
  }
125978
126073
  function balanceFields(parts) {
@@ -127083,7 +127178,7 @@ function createWalletTools({
127083
127178
  body: { type: "string", description: "Request body text (POST only)." },
127084
127179
  max_payment: { type: "string", description: 'Optional: the most this call may pay, in USDC, like "0.05"; at most the spending limit.' },
127085
127180
  max_payment_atomic: { type: "string", pattern: "^[1-9][0-9]*$", description: "The same in atomic units (1000000 = 1 USDC). Give max_payment or max_payment_atomic, not both with different values." },
127086
- request_id: { type: "string", pattern: "^[A-Za-z0-9._:-]{1,128}$", description: "Your stable name for this payment. Leave it out for a new payment; pass the same one to resume a failed payment without paying twice." }
127181
+ request_id: { type: "string", pattern: "^[A-Za-z0-9._:-]{1,128}$", description: "Optional. Leave it out for a new payment (the answer names the id it made). Pass the request_id of an earlier answer only to resume that payment; it never pays twice." }
127087
127182
  },
127088
127183
  required: ["url"],
127089
127184
  additionalProperties: false
@@ -127112,7 +127207,7 @@ ${canonical5}`;
127112
127207
  log("MCP_PAY_JOINED");
127113
127208
  return running;
127114
127209
  }
127115
- const task = payCall({ target, request, argument: argument2, requestId, given, calledAt, progress: progress3, canProgress, signal }).then((output2) => given ? output2 : withAutoId(output2, "x402_pay", requestId));
127210
+ const task = payCall({ target, request, argument: argument2, requestId, given, calledAt, progress: progress3, canProgress, signal });
127116
127211
  if (joinKey !== void 0) {
127117
127212
  paysRunning.set(joinKey, task);
127118
127213
  task.then(() => paysRunning.delete(joinKey), () => paysRunning.delete(joinKey));
@@ -127336,7 +127431,6 @@ ${canonical5}`;
127336
127431
  }
127337
127432
  const { bytes: bytes2, truncated } = await readBody(response);
127338
127433
  const payment = response.headers.get(PAYMENT_HEADER);
127339
- const autoId = given ? void 0 : requestId;
127340
127434
  if (!payment) {
127341
127435
  const moved2 = movedTo(response, target.url);
127342
127436
  return answerResult(paidAnswerContent({
@@ -127356,8 +127450,7 @@ ${canonical5}`;
127356
127450
  detail: `the URL answered HTTP ${response.status} without asking for payment; nothing was paid`,
127357
127451
  ...moved2 ? { movedTo: moved2, next: `nothing was paid: this URL answered HTTP ${response.status}, which says it moved to ${moved2}, and gave no data. Tell the user it moved. If the user wants it, call x402_pay again with url "${moved2}", the same method, headers and body, and a NEW request_id` } : response.status >= 200 && response.status < 300 ? {} : { next: `nothing was paid: this URL answered HTTP ${response.status} and did not ask for payment, so it gave no data; check the URL, or pay another seller` }
127358
127452
  },
127359
- "x402_pay",
127360
- autoId
127453
+ "x402_pay"
127361
127454
  )
127362
127455
  }));
127363
127456
  }
@@ -127430,13 +127523,9 @@ ${canonical5}`;
127430
127523
  ...balanceAnswer(one),
127431
127524
  ...takeRefundNotices(),
127432
127525
  next: receipt.replayed === true ? "this request id was already paid: the stored receipt came back and nothing new was paid" : floatPaid ? "paid through Shinjuku (privately: the chain shows Shinjuku paying the seller, not this wallet); tell the user the price (priceAtomic), the fee (feeAtomic), the total (amountAtomic), and the transaction (the seller's settle transaction; null when the seller returned none)" + (shinjukuTransaction ? ". shinjukuTransaction is this wallet's private payment to Shinjuku" : ". shinjukuTransaction (this wallet's private payment to Shinjuku) is not recorded yet; wallet_receipts shows it once it settles") + ". wallet_balance shows the new balance" : "paid; tell the user what was paid (amountAtomic, transaction). wallet_receipts lists this payment, wallet_balance shows the new balance"
127433
- }, "x402_pay", autoId)
127526
+ }, "x402_pay")
127434
127527
  }));
127435
127528
  }
127436
- const withIdSentence = (say3, id) => {
127437
- const line = `${say3} Its request_id is ${id}.`;
127438
- return line.trim().split(/\s+/).length <= SAY_MAX_WORDS ? line : say3;
127439
- };
127440
127529
  function networkFields() {
127441
127530
  const mode = runNetworkMode();
127442
127531
  let onion = false;
@@ -127447,9 +127536,8 @@ ${canonical5}`;
127447
127536
  }
127448
127537
  return { network: mode === "tor" ? { mode: "tor", detail: `every request of this wallet goes through Tor (checked at start)${onion ? "; the facilitator is reached over its onion service, so no Tor exit sees those requests" : ""}` } : mode === "proxy" ? { mode: "proxy", detail: "every request of this wallet goes through the proxy this wallet was started with; it is not checked as Tor" } : { mode: "direct", detail: `no Tor: the facilitator, the RPC provider and each seller see this computer's IP address. To hide it, the user runs ${terminalCommand2("setup --yes --prefer-tor")} once in a terminal (it records Tor and checks the wallet's files again; nothing else changes; if Tor is not running, it says how to start it), then closes this app and opens it again` } };
127449
127538
  }
127450
- function sayFirst(fields, tool, autoId) {
127451
- let say3 = sayFor(fields, { tool, npmVersion });
127452
- if (autoId) say3 = withIdSentence(say3, autoId);
127539
+ function sayFirst(fields, tool) {
127540
+ const say3 = sayFor(fields, { tool, npmVersion });
127453
127541
  const usdc6 = usdcFields(fields);
127454
127542
  let news = [];
127455
127543
  try {
@@ -127459,21 +127547,19 @@ ${canonical5}`;
127459
127547
  }
127460
127548
  return { say: say3, ...fields, ...Object.keys(usdc6).length ? { usdc: usdc6 } : {}, ...Array.isArray(news) && news.length ? { news } : {} };
127461
127549
  }
127462
- function withAutoId(output2, tool, requestId, nextNote) {
127550
+ function withAutoId(output2, tool, nextNote) {
127463
127551
  const said = addSay(output2, tool, { news: watcher, npmVersion });
127464
127552
  const blocks = Array.isArray(said?.content) && said.content.length ? said.content : void 0;
127465
127553
  const text3 = blocks ? blocks[0]?.type === "text" ? blocks[0].text : void 0 : said?.text;
127466
127554
  if (typeof text3 !== "string" || text3.length > 262144 || !/^\s*\{\s*"say"\s*:/.test(text3.slice(0, 64))) return said;
127467
- const named = text3.slice(0, 600).includes(`Its request_id is ${requestId}.`);
127468
- if (named && !nextNote) return said;
127469
127555
  let parsed2;
127470
127556
  try {
127471
127557
  parsed2 = JSON.parse(text3);
127472
127558
  } catch {
127473
127559
  return said;
127474
127560
  }
127475
- if (!named && typeof parsed2.say === "string") parsed2.say = withIdSentence(parsed2.say, requestId);
127476
- if (nextNote && typeof parsed2.next === "string") parsed2.next = `${parsed2.next.replace(/[.\s]+$/, "")}. ${nextNote}`;
127561
+ if (typeof parsed2.next !== "string") return said;
127562
+ parsed2.next = `${parsed2.next.replace(/[.\s]+$/, "")}. ${nextNote}`;
127477
127563
  const out = JSON.stringify(parsed2, null, /^\s*\{\s*\n/.test(text3) ? 2 : void 0);
127478
127564
  return { ...said, text: out, ...blocks ? { content: [{ ...blocks[0], text: out }, ...blocks.slice(1)] } : {} };
127479
127565
  }
@@ -128364,6 +128450,7 @@ ${canonical5}`;
128364
128450
  privateSends.push({
128365
128451
  requestId: transfer ? plan.transferTokenId : shownId(plan.mcpRequestId, recordId2),
128366
128452
  ...transfer ? { tool: "wallet_transfer_token" } : {},
128453
+ to: plan.mcpTo,
128367
128454
  amountAtomic: plan.amountAtomic,
128368
128455
  sentAtomic: send3.sentAtomic,
128369
128456
  leftAtomic: send3.remainingAtomic,
@@ -128410,19 +128497,20 @@ ${canonical5}`;
128410
128497
  })
128411
128498
  }));
128412
128499
  if (sendWaiting) {
128413
- 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`;
128414
128501
  }
128415
128502
  const goingSends = privateSends.filter((send3) => send3.failedTries === void 0);
128416
128503
  if (goingSends.length) {
128417
- 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("; ")}`;
128418
128505
  }
128419
128506
  const failingSends = privateSends.filter((send3) => send3.failedTries > 0);
128420
128507
  if (failingSends.length) {
128421
- 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(". ");
128422
128509
  }
128423
128510
  if (privateSends.length && one.largestPaymentAtomic === "0" && (one.changeSettling !== true || one.changeUpToAtomic !== void 0) && /^[1-9][0-9]*$/.test(String(one.balanceAtomic ?? ""))) {
128424
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, `;
128425
- 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`;
128426
128514
  }
128427
128515
  const balanceParts = parts?.parts > 1 && ATOMIC_TEXT.test(String(one.balanceAtomic ?? "")) ? parts.parts : void 0;
128428
128516
  if (balanceParts && !sendWaiting) {
@@ -128862,7 +128950,7 @@ ${canonical5}`;
128862
128950
  live = "unreadable";
128863
128951
  }
128864
128952
  const started = Number.isSafeInteger(record.createdAtMs) ? record.createdAtMs : Number.isSafeInteger(joining?.createdAtMs) ? joining.createdAtMs : void 0;
128865
- 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);
128866
128954
  const shown = shownId(record.mcpRequestId, idAfter(name, "self-pay-mcp-"));
128867
128955
  if (live !== "joining") joining = void 0;
128868
128956
  const retry = joining ? retryFields(joining) : void 0;
@@ -130381,7 +130469,7 @@ ${canonical5}`;
130381
130469
  if (!record) continue;
130382
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);
130383
130471
  if (!finished) return void 0;
130384
- const at = Date.parse(String(record.doneAt ?? "")) || record.exitedAtMs;
130472
+ const at = Date.parse(String(record.doneAt ?? "")) || sentAtMs(record);
130385
130473
  if (Number.isSafeInteger(at)) return at;
130386
130474
  try {
130387
130475
  return Math.floor(statSync(join32(store.walDirectory, `${name}.json`)).mtimeMs);
@@ -130411,7 +130499,6 @@ ${canonical5}`;
130411
130499
  return withAutoId(
130412
130500
  answer,
130413
130501
  spec.tool,
130414
- claimed.requestId,
130415
130502
  "The same call without request_id within 10 minutes after it ends reads this same request; to do it again later, ask after 10 minutes or give a new request_id."
130416
130503
  );
130417
130504
  }
@@ -130569,7 +130656,7 @@ ${canonical5}`;
130569
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);
130570
130657
  if (finished) {
130571
130658
  const send3 = split4 ? privateSendStatus(store, record) : void 0;
130572
- const endedAtMs = Date.parse(String(record.doneAt ?? record.cancelledAt ?? "")) || record.exitedAtMs;
130659
+ const endedAtMs = Date.parse(String(record.doneAt ?? record.cancelledAt ?? "")) || sentAtMs(record);
130573
130660
  const earlier = kind !== "elsewhere" ? { replayed: true, newRequestId: freshId, ...Number.isSafeInteger(endedAtMs) ? { finishedMinutesAgo: Math.max(0, Math.round((Date.now() - endedAtMs) / 6e4)) } : {} } : {};
130574
130661
  const earlierWords = earlier.finishedMinutesAgo !== void 0 ? ` about ${minutesText2(earlier.finishedMinutesAgo)} ago` : " earlier";
130575
130662
  return json3({
@@ -133271,7 +133358,7 @@ ${canonical5}`;
133271
133358
  temperature: { type: "number", minimum: 0, maximum: 2, description: "Sampling temperature; leave it out for the model's default." },
133272
133359
  max_payment: { type: "string", description: 'Optional: the most this call may pay, in USDC, like "0.05"; at most the spending limit.' },
133273
133360
  max_payment_atomic: { type: "string", pattern: "^[1-9][0-9]*$", description: "The most this call may pay, in atomic units; at most the spending limit." },
133274
- request_id: { type: "string", pattern: "^[A-Za-z0-9._:-]{1,128}$", description: "Your stable name for this call. Leave it out for a new call; pass the same one to resume a failed call without paying twice." }
133361
+ request_id: { type: "string", pattern: "^[A-Za-z0-9._:-]{1,128}$", description: "Optional. Leave it out for a new question (the answer names the id it made). Pass the request_id of an earlier answer only to resume that call; it never pays twice." }
133275
133362
  },
133276
133363
  additionalProperties: false
133277
133364
  },
@@ -134764,7 +134851,7 @@ var init_tools = __esm({
134764
134851
  shownId = (agentId, rid) => typeof agentId === "string" && REQUEST_ID4.test(agentId) && toRecordId(agentId) === rid ? agentId : rid;
134765
134852
  isSplitSend = (request) => Boolean(request) && !isSingleSend(request.parts ?? 1, request.maxDelayMinutes ?? 0);
134766
134853
  sendPartId = (requestId) => (index) => `mp${index}-${toRecordId(requestId)}`;
134767
- 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";
134768
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";
134769
134856
  sendAnswerOf = (status) => {
134770
134857
  const { nextPartLastTryAt: _clock, ...shown } = status;