shinjuku-shielded 0.3.156 → 0.3.157
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 +6 -6
- package/manifest.json +4 -4
- package/package.json +3 -3
- package/shinjuku-wallet.mjs +263 -64
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.
|
|
9
|
+
This is version 0.3.157. 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.
|
|
15
|
+
npm install -g shinjuku@0.3.157
|
|
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.
|
|
21
|
+
installing, type `npx shinjuku@0.3.157 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.
|
|
39
|
+
claude mcp add --scope user shinjuku -- npx -y --no-audit --no-fund shinjuku-shield@0.3.157 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.
|
|
45
|
+
{ "mcpServers": { "shinjuku": { "command": "npx", "args": ["-y", "--no-audit", "--no-fund", "shinjuku-shield@0.3.157", "mcp"] } } }
|
|
46
46
|
```
|
|
47
47
|
|
|
48
48
|
On native Windows, if the host cannot find `npx`, use `"command": "cmd"` and
|
|
@@ -55,7 +55,7 @@ Codex (the Codex app and Codex CLI read the same file, `~/.codex/config.toml`):
|
|
|
55
55
|
```toml
|
|
56
56
|
[mcp_servers.shinjuku]
|
|
57
57
|
command = "npx"
|
|
58
|
-
args = ["-y", "--no-audit", "--no-fund", "shinjuku-shield@0.3.
|
|
58
|
+
args = ["-y", "--no-audit", "--no-fund", "shinjuku-shield@0.3.157", "mcp"]
|
|
59
59
|
startup_timeout_sec = 120
|
|
60
60
|
tool_timeout_sec = 300
|
|
61
61
|
```
|
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": "
|
|
5
|
-
"bytes":
|
|
6
|
-
"sourceCommit": "
|
|
4
|
+
"sha256": "46b6176b85190a52572566a760686111c9f01d07b8057e83a3ea1d76a20a3f5f",
|
|
5
|
+
"bytes": 9069628,
|
|
6
|
+
"sourceCommit": "4c41d8d68ac102c3b34f13def9057f631c923ce7",
|
|
7
7
|
"sourceDirty": false,
|
|
8
8
|
"installRecordMatchesLockfile": true,
|
|
9
9
|
"releaseRule": "valid only when two fresh npm ci --ignore-scripts builds on different OSes give the same sha256; installRecordMatchesLockfile true means every entry npm installed equals its package-lock.json entry and every entry not marked optional, devOptional included, is installed: an optional entry (a package for another platform) may be absent",
|
|
@@ -1038,5 +1038,5 @@
|
|
|
1038
1038
|
"version": "1.8.0"
|
|
1039
1039
|
}
|
|
1040
1040
|
],
|
|
1041
|
-
"publishedAt": "2026-11-
|
|
1041
|
+
"publishedAt": "2026-11-03T09:32:37.020Z"
|
|
1042
1042
|
}
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "shinjuku-shielded",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.157",
|
|
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": "
|
|
7
|
-
"shinjukuSha256": "
|
|
6
|
+
"shinjukuRelease": "46b6176b",
|
|
7
|
+
"shinjukuSha256": "46b6176b85190a52572566a760686111c9f01d07b8057e83a3ea1d76a20a3f5f",
|
|
8
8
|
"license": "SEE LICENSE IN LICENSE.txt",
|
|
9
9
|
"author": "Shinjuku Shielded",
|
|
10
10
|
"homepage": "https://github.com/ShinjukuStaition/shinjuku-shield#readme",
|
package/shinjuku-wallet.mjs
CHANGED
|
@@ -67027,6 +67027,7 @@ var init_errorCodes = __esm({
|
|
|
67027
67027
|
"inference_no_provider",
|
|
67028
67028
|
"inference_origin_refused",
|
|
67029
67029
|
"inference_out_of_tokens",
|
|
67030
|
+
"inference_empty_answer",
|
|
67030
67031
|
// wallet_unshield of a balance in several accounts: the stage code while the automatic join runs (mcp/tools.mjs joiningStage).
|
|
67031
67032
|
"unshield_joining_accounts",
|
|
67032
67033
|
// unshield of a fixed amount in a terminal: its own join (joinForFixedSend, AB186) did not get the balance ready.
|
|
@@ -83582,15 +83583,19 @@ function memoryRequestMatches(record, { url, request, programId, anyProgram = fa
|
|
|
83582
83583
|
function dropOldAnswers(now) {
|
|
83583
83584
|
for (const [key, entry] of answers) if (now >= entry.atMs + MEMORY_ANSWER_KEEP_MS) answers.delete(key);
|
|
83584
83585
|
}
|
|
83585
|
-
function keepMemoryAnswer(record, resultBase64, now = Date.now()) {
|
|
83586
|
+
function keepMemoryAnswer(record, resultBase64, now = Date.now(), sellerHeaders = []) {
|
|
83586
83587
|
if (!isMemoryFingerprint(record?.fingerprint) || typeof resultBase64 !== "string") return;
|
|
83587
83588
|
dropOldAnswers(now);
|
|
83588
|
-
keepBounded(answers, record.fingerprint, { resultBase64, atMs: now });
|
|
83589
|
+
keepBounded(answers, record.fingerprint, { resultBase64, atMs: now, sellerHeaders: Array.isArray(sellerHeaders) ? sellerHeaders : [] });
|
|
83589
83590
|
}
|
|
83590
83591
|
function memoryAnswer(record, now = Date.now()) {
|
|
83591
83592
|
dropOldAnswers(now);
|
|
83592
83593
|
return isMemoryFingerprint(record?.fingerprint) ? answers.get(record.fingerprint)?.resultBase64 : void 0;
|
|
83593
83594
|
}
|
|
83595
|
+
function memoryAnswerHeaders(record, now = Date.now()) {
|
|
83596
|
+
dropOldAnswers(now);
|
|
83597
|
+
return (isMemoryFingerprint(record?.fingerprint) ? answers.get(record.fingerprint)?.sellerHeaders : void 0) ?? [];
|
|
83598
|
+
}
|
|
83594
83599
|
function forgetMemoryOnly() {
|
|
83595
83600
|
requests.clear();
|
|
83596
83601
|
answers.clear();
|
|
@@ -102221,7 +102226,7 @@ function floatOutcomeWords(code2, error, t) {
|
|
|
102221
102226
|
case "p03_float_answer_invalid_refund_pending": {
|
|
102222
102227
|
const amount = amountText(error?.paidAtomic, "the amount paid");
|
|
102223
102228
|
const fixable = floatSellerCanBeFixed(error);
|
|
102224
|
-
const seller = fixable && typeof error?.sellerReason === "string" ? ` ${p.The} refused this request (HTTP ${error.httpStatus}) and said why: ${JSON.stringify(error.sellerReason)}.` : fixable ? ` ${p.The} refused this request (HTTP ${error.httpStatus}) and said why in its first answer; the wallet does not keep ${p.the}'s words.` : typeof error?.sellerReason === "string" ? ` ${p.The} answered HTTP ${error.httpStatus}: ${JSON.stringify(error.sellerReason)}.` : Number.isInteger(error?.httpStatus) ? ` ${p.The} answered HTTP ${error.httpStatus}.` : "";
|
|
102229
|
+
const seller = fixable && error?.sellerReasonHidden === true ? ` ${p.The} refused this request (HTTP ${error.httpStatus}); its own words are not shown, because they can repeat your question.` : fixable && typeof error?.sellerReason === "string" ? ` ${p.The} refused this request (HTTP ${error.httpStatus}) and said why: ${JSON.stringify(error.sellerReason)}.` : fixable ? ` ${p.The} refused this request (HTTP ${error.httpStatus}) and said why in its first answer; the wallet does not keep ${p.the}'s words.` : typeof error?.sellerReason === "string" ? ` ${p.The} answered HTTP ${error.httpStatus}: ${JSON.stringify(error.sellerReason)}.` : Number.isInteger(error?.httpStatus) ? ` ${p.The} answered HTTP ${error.httpStatus}.` : "";
|
|
102225
102230
|
const late2 = floatChargeTooLate(error) ? ` ${p.The} worked ${error.sellerAnswerSeconds} s before it tried to charge, and its answer says the charge failed. A Solana payment expires about 30 s after it is signed, so no wallet can pay ${p.a} that works this long before it charges.` : "";
|
|
102226
102231
|
const tryAgain = fixable ? t.ai ? `${p.The} said what is wrong with the request. If ${stillWants(t)} an answer, change the question as it says, then ${t.rerun.toLowerCase()} with ${t.newId}` : `The seller said what is wrong with the request. If ${stillWants(t)} the data, fix the request as the seller says (for example add the query values, headers, or body fields it names), then ${t.rerun.toLowerCase()} with the fixed request and ${t.newId}` : late2 ? t.ai ? `${p.The} works too long before it charges. ${t.ai.other}` : "Do not pay this seller again: it works too long before it charges" : t.ai ? `If ${stillWants(t)} an answer, ${t.askNew}` : `If ${stillWants(t)} the data, try this seller again with ${t.newId}`;
|
|
102227
102232
|
const why = floatRefundWhy(error, Boolean(t.ai));
|
|
@@ -102398,7 +102403,7 @@ function floatOutcomeWords(code2, error, t) {
|
|
|
102398
102403
|
return void 0;
|
|
102399
102404
|
}
|
|
102400
102405
|
}
|
|
102401
|
-
var ATOMIC4, TRANSFER_DEAD_WORDS, payeeOf, floatSellerCanBeFixed, floatSeller4xx, answeredInstead, floatStillSettling, floatChargeTooLate, amountText, withTransaction, PAID_SELLER_FAILED_CODE, stillWants, AI_SERVER_WRONG, QUOTE_REFUSED_WHY;
|
|
102406
|
+
var ATOMIC4, TRANSFER_DEAD_WORDS, payeeOf, floatSellerCanBeFixed, withoutProviderText, floatSeller4xx, answeredInstead, floatStillSettling, floatChargeTooLate, amountText, withTransaction, PAID_SELLER_FAILED_CODE, stillWants, AI_SERVER_WRONG, QUOTE_REFUSED_WHY;
|
|
102402
102407
|
var init_floatWords = __esm({
|
|
102403
102408
|
"wallet/src/pay/floatWords.mjs"() {
|
|
102404
102409
|
"use strict";
|
|
@@ -102411,6 +102416,11 @@ var init_floatWords = __esm({
|
|
|
102411
102416
|
};
|
|
102412
102417
|
payeeOf = (ai) => ai ? { the: "the AI provider", The: "The AI provider", this: "the AI provider", a: "an AI provider" } : { the: "the seller", The: "The seller", this: "this seller", a: "a seller" };
|
|
102413
102418
|
floatSellerCanBeFixed = (error) => floatSeller4xx(error) && !floatChargeTooLate(error) && ![402, 408, 429].includes(error.httpStatus) && (typeof error?.sellerReason === "string" && error.sellerReason.trim() !== "" || error?.sellerFixable === true);
|
|
102419
|
+
withoutProviderText = (facts) => {
|
|
102420
|
+
const { sellerReason, ...rest } = facts ?? {};
|
|
102421
|
+
if (typeof sellerReason !== "string" || !sellerReason.trim()) return rest;
|
|
102422
|
+
return { ...rest, sellerReasonHidden: true, ...floatSellerCanBeFixed(facts) ? { sellerFixable: true } : {} };
|
|
102423
|
+
};
|
|
102414
102424
|
floatSeller4xx = (error) => Number.isInteger(error?.httpStatus) && error.httpStatus >= 400 && error.httpStatus < 500;
|
|
102415
102425
|
answeredInstead = (p, status, data) => `${p.the} answered HTTP ${status}${status >= 500 ? " (an error on its side)" : ""} instead of ${data}`;
|
|
102416
102426
|
floatStillSettling = (code2, error) => code2 === "p03_float_paid_finishing_retry_same_request" && error?.sellerCode === "payment_pending_retry_same_payment";
|
|
@@ -103682,10 +103692,10 @@ function markFloatRefundOutcomesShown(store, shown) {
|
|
|
103682
103692
|
}
|
|
103683
103693
|
}
|
|
103684
103694
|
function toMemoryOnly(store, walName2, record) {
|
|
103685
|
-
const { resultBase64, resultSha256: _hash, sellerReason: _reason, ...rest } = record;
|
|
103695
|
+
const { resultBase64, resultSha256: _hash, sellerReason: _reason, sellerHeaders, ...rest } = record;
|
|
103686
103696
|
const next = { ...rest, fingerprint: memoryOnlyFingerprint(), request: memoryOnlyRequest(record.request), memoryOnly: true };
|
|
103687
103697
|
store.writeWal(walName2, next);
|
|
103688
|
-
if (next.state === "receipt") keepMemoryAnswer(next, resultBase64);
|
|
103698
|
+
if (next.state === "receipt") keepMemoryAnswer(next, resultBase64, void 0, sellerHeaders);
|
|
103689
103699
|
return next;
|
|
103690
103700
|
}
|
|
103691
103701
|
function finishedAtMs(record) {
|
|
@@ -104446,7 +104456,7 @@ async function payViaFloat({
|
|
|
104446
104456
|
if (record.state === "receipt" && record.memoryOnly === true) {
|
|
104447
104457
|
const kept3 = memoryAnswer(record);
|
|
104448
104458
|
if (kept3 === void 0) finished(record);
|
|
104449
|
-
return paidResult({ ...record, resultBase64: kept3 }, true);
|
|
104459
|
+
return paidResult({ ...record, resultBase64: kept3, sellerHeaders: memoryAnswerHeaders(record) }, true);
|
|
104450
104460
|
}
|
|
104451
104461
|
if (record.state === "receipt") return paidResult(record, true);
|
|
104452
104462
|
if (record.state === "refund_pending") refundPendingAnswer(record);
|
|
@@ -104823,18 +104833,18 @@ async function payViaFloat({
|
|
|
104823
104833
|
transaction: isSolanaSignature(settled?.transaction) ? settled.transaction : null,
|
|
104824
104834
|
network: record.requirement.network
|
|
104825
104835
|
};
|
|
104836
|
+
const sellerHeaders = keptSellerHeaders(response.headers);
|
|
104826
104837
|
record = {
|
|
104827
104838
|
...record,
|
|
104828
104839
|
state: "receipt",
|
|
104829
104840
|
status: response.status,
|
|
104830
104841
|
receipt,
|
|
104831
|
-
...answerFields(record, resultBase64),
|
|
104832
|
-
sellerHeaders: keptSellerHeaders(response.headers),
|
|
104842
|
+
...answerFields(record, resultBase64, sellerHeaders),
|
|
104833
104843
|
answeredAtMs: Date.now()
|
|
104834
104844
|
};
|
|
104835
104845
|
store.writeWal(walName2, record);
|
|
104836
104846
|
mark("float-receipt");
|
|
104837
|
-
return answered(store, walName2, record, resultBase64);
|
|
104847
|
+
return answered(store, walName2, record, resultBase64, sellerHeaders);
|
|
104838
104848
|
}
|
|
104839
104849
|
const sellerReason = await sellerAnswerText(response);
|
|
104840
104850
|
if (wasKept) keptAgain(response.status, sellerReason);
|
|
@@ -105105,18 +105115,18 @@ async function selfBroadcastLeg({ store, walName: walName2, record, sendRequest,
|
|
|
105105
105115
|
transaction: isSolanaSignature(settled?.transaction) ? settled.transaction : record.broadcastSignature,
|
|
105106
105116
|
network: record.requirement.network
|
|
105107
105117
|
};
|
|
105118
|
+
const sellerHeaders = keptSellerHeaders(response.headers);
|
|
105108
105119
|
record = {
|
|
105109
105120
|
...record,
|
|
105110
105121
|
state: "receipt",
|
|
105111
105122
|
status: response.status,
|
|
105112
105123
|
receipt,
|
|
105113
|
-
...answerFields(record, resultBase64),
|
|
105114
|
-
sellerHeaders: keptSellerHeaders(response.headers),
|
|
105124
|
+
...answerFields(record, resultBase64, sellerHeaders),
|
|
105115
105125
|
answeredAtMs: Date.now()
|
|
105116
105126
|
};
|
|
105117
105127
|
store.writeWal(walName2, record);
|
|
105118
105128
|
mark("float-receipt");
|
|
105119
|
-
return answered(store, walName2, record, resultBase64);
|
|
105129
|
+
return answered(store, walName2, record, resultBase64, sellerHeaders);
|
|
105120
105130
|
}
|
|
105121
105131
|
if (response.status >= 500 || response.status === 202) {
|
|
105122
105132
|
const unansweredReason = await sellerRefusalText(response);
|
|
@@ -105188,13 +105198,13 @@ function keptSellerHeaders(headers) {
|
|
|
105188
105198
|
}
|
|
105189
105199
|
return kept3;
|
|
105190
105200
|
}
|
|
105191
|
-
function answerFields(record, resultBase64) {
|
|
105192
|
-
return record.memoryOnly === true ? {} : { resultBase64 };
|
|
105201
|
+
function answerFields(record, resultBase64, sellerHeaders) {
|
|
105202
|
+
return record.memoryOnly === true ? {} : { resultBase64, sellerHeaders };
|
|
105193
105203
|
}
|
|
105194
|
-
function answered(store, walName2, record, resultBase64) {
|
|
105204
|
+
function answered(store, walName2, record, resultBase64, sellerHeaders) {
|
|
105195
105205
|
if (record.memoryOnly !== true) return paidResult(record, false);
|
|
105196
|
-
keepMemoryAnswer(record, resultBase64);
|
|
105197
|
-
return paidResult({ ...record, resultBase64 }, false);
|
|
105206
|
+
keepMemoryAnswer(record, resultBase64, void 0, sellerHeaders);
|
|
105207
|
+
return paidResult({ ...record, resultBase64, sellerHeaders }, false);
|
|
105198
105208
|
}
|
|
105199
105209
|
function paidResult(record, replayed) {
|
|
105200
105210
|
return {
|
|
@@ -105288,7 +105298,7 @@ var init_floatPay = __esm({
|
|
|
105288
105298
|
floatPaymentWalName = (requestKey) => `${FLOAT_PAYMENT_WAL_PREFIX2}${requestKey}`;
|
|
105289
105299
|
floatRecordName = (requestId) => floatPaymentWalName(hash7(requestId));
|
|
105290
105300
|
FLOAT_KEEP_BYTES_MS = 6e5;
|
|
105291
|
-
memoryOnlyNeedsScrub = (record) => record?.memoryOnly === true && (record.resultSha256 !== void 0 || record.request?.bodySha256 !== void 0 || record.request?.headersSha256 !== void 0 || !/^memory-[0-9a-f]{64}$/.test(String(record.fingerprint ?? "")));
|
|
105301
|
+
memoryOnlyNeedsScrub = (record) => record?.memoryOnly === true && (record.resultSha256 !== void 0 || Array.isArray(record.sellerHeaders) && record.sellerHeaders.length > 0 || record.request?.bodySha256 !== void 0 || record.request?.headersSha256 !== void 0 || !/^memory-[0-9a-f]{64}$/.test(String(record.fingerprint ?? "")));
|
|
105292
105302
|
FLOAT_KEEP_OPEN_REQUEST_MS = 36e5;
|
|
105293
105303
|
configCache = /* @__PURE__ */ new Map();
|
|
105294
105304
|
configGeneration = 0;
|
|
@@ -109497,6 +109507,16 @@ function refusalSay(a, tool, again, npmVersion) {
|
|
|
109497
109507
|
const head = `${paid ? `Paid ${paid} USDC${price2 && positive(fee) ? ` (${price2} price + ${fee} Shinjuku fee)` : ""}` : "Paid"}, but`;
|
|
109498
109508
|
return firstFit(`${head} the model reached the answer's length limit before it answered.${more}`, `${head} the answer hit its length limit.${more}`);
|
|
109499
109509
|
}
|
|
109510
|
+
if (code2 === "inference_empty_answer") {
|
|
109511
|
+
const again2 = " Ask again (it pays again) or pick another model.";
|
|
109512
|
+
if (a.replayed === true && a.paid === false) return `Nothing was paid, and the stored answer is empty.${again2}`;
|
|
109513
|
+
if (a.replayed === true) return `Already paid; the stored answer is empty and nothing new was paid.${again2}`;
|
|
109514
|
+
const paid = usdcText2(a.paidAtomic);
|
|
109515
|
+
const price2 = usdcText2(a.priceAtomic);
|
|
109516
|
+
const fee = usdcText2(a.feeAtomic);
|
|
109517
|
+
const head = `${paid ? `Paid ${paid} USDC${price2 && positive(fee) ? ` (${price2} price + ${fee} Shinjuku fee)` : ""}` : "Paid"}, but`;
|
|
109518
|
+
return firstFit(`${head} the AI returned no answer, and no refund comes.${again2}`, `${head} the AI returned no answer.${again2}`);
|
|
109519
|
+
}
|
|
109500
109520
|
if (code2 === "inference_no_provider") {
|
|
109501
109521
|
return "Nothing was paid: no AI provider can take this wallet's payment for this model right now. Ask for another model, or ask again later.";
|
|
109502
109522
|
}
|
|
@@ -110953,8 +110973,8 @@ async function offeredPrice(paymentRequired, store, serviceUrl2, target, timing,
|
|
|
110953
110973
|
}
|
|
110954
110974
|
return { rail, amountAtomic: highest.toString(), priceAtomic: highest.toString() };
|
|
110955
110975
|
}
|
|
110956
|
-
function ownRecord(store, requestId) {
|
|
110957
|
-
const float = floatRecordOf(store, requestId);
|
|
110976
|
+
function ownRecord(store, requestId, { strict = false } = {}) {
|
|
110977
|
+
const float = strict ? store.readWal(floatRecordName(requestId)) : floatRecordOf(store, requestId);
|
|
110958
110978
|
if (float) return { kind: "float", record: float };
|
|
110959
110979
|
const key = sha256Hex2(Buffer.from(requestId, "utf8"));
|
|
110960
110980
|
const shielded = store.readWal(`resource-payment-${key}`);
|
|
@@ -111492,6 +111512,23 @@ async function paidCall({ session, target, method, headers, body, cap, maxPrice,
|
|
|
111492
111512
|
}
|
|
111493
111513
|
mark("fetch-ready");
|
|
111494
111514
|
const fresh = challenge !== void 0 && Date.now() - challenge.atMs <= PREFETCH_MAX_AGE_MS;
|
|
111515
|
+
const leftDecision = () => {
|
|
111516
|
+
let left = true;
|
|
111517
|
+
let unreadable = false;
|
|
111518
|
+
let after;
|
|
111519
|
+
try {
|
|
111520
|
+
after = ownRecord(store, requestId, { strict: true });
|
|
111521
|
+
left = after === void 0 ? own !== void 0 : committed(after, store) && !closedUnpaid(after, store) && !preparedOnly(after, store);
|
|
111522
|
+
} catch {
|
|
111523
|
+
after = void 0;
|
|
111524
|
+
unreadable = true;
|
|
111525
|
+
}
|
|
111526
|
+
const atomic4 = left ? (after === void 0 ? void 0 : paidAmount(after) ?? price2?.amountAtomic) ?? String(cap) : void 0;
|
|
111527
|
+
return Object.freeze({ left, atomic: atomic4, unreadable, before: leftBefore });
|
|
111528
|
+
};
|
|
111529
|
+
const recordLeft = (decision) => {
|
|
111530
|
+
if (policy && decision.left) policy.record({ amountAtomic: earlierRun ? "0" : decision.atomic, url: target, requestId });
|
|
111531
|
+
};
|
|
111495
111532
|
const result = await payResource({
|
|
111496
111533
|
store,
|
|
111497
111534
|
connection: session.connection,
|
|
@@ -111535,19 +111572,54 @@ async function paidCall({ session, target, method, headers, body, cap, maxPrice,
|
|
|
111535
111572
|
onCompactList: memoPrefetchOnList(session, store)
|
|
111536
111573
|
})
|
|
111537
111574
|
}
|
|
111538
|
-
}).finally(markMade)
|
|
111539
|
-
|
|
111575
|
+
}).finally(markMade).catch((error) => {
|
|
111576
|
+
const failure2 = error !== null && typeof error === "object" ? error : Object.assign(new Error(String(error)), { cause: error });
|
|
111577
|
+
try {
|
|
111578
|
+
const decision = leftDecision();
|
|
111579
|
+
try {
|
|
111580
|
+
failure2.paidLeft = decision;
|
|
111581
|
+
} catch {
|
|
111582
|
+
}
|
|
111583
|
+
recordLeft(decision);
|
|
111584
|
+
} catch {
|
|
111585
|
+
}
|
|
111586
|
+
throw failure2;
|
|
111587
|
+
});
|
|
111588
|
+
let ownAfter;
|
|
111589
|
+
let afterUnreadable = false;
|
|
111590
|
+
try {
|
|
111591
|
+
ownAfter = ownRecord(store, requestId, { strict: true });
|
|
111592
|
+
} catch {
|
|
111593
|
+
ownAfter = void 0;
|
|
111594
|
+
afterUnreadable = true;
|
|
111595
|
+
}
|
|
111596
|
+
const resultAtomic = POSITIVE_ATOMIC4.test(String(result.paidAtomic ?? "")) ? String(result.paidAtomic) : void 0;
|
|
111597
|
+
const amountAtomic = paidAmount(ownAfter) ?? resultAtomic ?? price2?.amountAtomic ?? (result.replayed ? void 0 : String(cap));
|
|
111540
111598
|
if (!result.replayed && policy && amountAtomic) policy.record({ amountAtomic: earlierRun ? "0" : amountAtomic, url: target, requestId });
|
|
111541
|
-
|
|
111542
|
-
|
|
111543
|
-
|
|
111544
|
-
|
|
111545
|
-
|
|
111546
|
-
|
|
111547
|
-
|
|
111548
|
-
|
|
111549
|
-
|
|
111550
|
-
|
|
111599
|
+
try {
|
|
111600
|
+
const bytes2 = Buffer.from(result.resultBase64 ?? "", "base64");
|
|
111601
|
+
const payment = {
|
|
111602
|
+
transaction: result.receipt?.transaction,
|
|
111603
|
+
scheme: result.scheme === "exact" ? "exact" : "shielded-exact",
|
|
111604
|
+
amountAtomic: amountAtomic ?? null,
|
|
111605
|
+
requestId,
|
|
111606
|
+
replayed: result.replayed === true,
|
|
111607
|
+
receiptFinality: result.receiptFinality ?? null
|
|
111608
|
+
};
|
|
111609
|
+
return new Response(bytes2, { status: 200, headers: paidResponseHeaders(result.sellerHeaders, sniffContentType(bytes2), payment) });
|
|
111610
|
+
} catch (error) {
|
|
111611
|
+
const failure2 = error !== null && typeof error === "object" ? error : Object.assign(new Error(String(error)), { cause: error });
|
|
111612
|
+
try {
|
|
111613
|
+
failure2.paidLeft = Object.freeze({
|
|
111614
|
+
left: true,
|
|
111615
|
+
atomic: amountAtomic ?? String(cap),
|
|
111616
|
+
unreadable: afterUnreadable,
|
|
111617
|
+
before: leftBefore || result.replayed === true
|
|
111618
|
+
});
|
|
111619
|
+
} catch {
|
|
111620
|
+
}
|
|
111621
|
+
throw failure2;
|
|
111622
|
+
}
|
|
111551
111623
|
} finally {
|
|
111552
111624
|
await release();
|
|
111553
111625
|
}
|
|
@@ -139048,6 +139120,21 @@ ${canonical5}`;
|
|
|
139048
139120
|
"inference_chat"
|
|
139049
139121
|
)), paidStore);
|
|
139050
139122
|
}
|
|
139123
|
+
if (typeof content === "string" && !content.trim()) {
|
|
139124
|
+
const paidText = usdcText3(paidAtomic ?? "");
|
|
139125
|
+
const reasonLogged = result.finishReason === void 0 || result.finishReason === null ? "none" : ["stop", "length", "content_filter", "tool_calls", "function_call"].includes(result.finishReason) ? result.finishReason : "other";
|
|
139126
|
+
log(`MCP_INFERENCE_EMPTY requestId=${requestId} finishReason=${reasonLogged}`);
|
|
139127
|
+
return withRefundNotices(json3(sayFirst(
|
|
139128
|
+
{
|
|
139129
|
+
ok: false,
|
|
139130
|
+
error: "inference_empty_answer",
|
|
139131
|
+
detail: `the AI provider returned an empty answer (finish reason ${JSON.stringify(result.finishReason ?? null)}); ` + (unpaid ? "nothing was paid: this request id's payment never landed (the pool's public history proves it)" : `this call was paid${paidText ? ` (${paidText} USDC)` : ""}${transaction ? ` in transaction ${transaction}` : ""} and no refund comes`),
|
|
139132
|
+
...paidFields,
|
|
139133
|
+
next: unpaid ? "nothing was paid, and this request id's stored answer is empty. If the user still wants an answer, call inference_chat with a NEW request_id, or another model; it pays then" : receipt.replayed === true ? "this request id was already paid and its stored answer is empty; nothing new was paid. If the user still wants an answer, call inference_chat with a NEW request_id, or another model; it pays again" : `Tell the user: the AI returned no answer, and this call paid ${paidText ? `${paidText} USDC` : "(see paidAtomic)"}; no refund comes. If the user still wants an answer, call inference_chat again with a NEW request_id (reword the question, or pick another model); it pays again. The same request_id returns this same empty answer`
|
|
139134
|
+
},
|
|
139135
|
+
"inference_chat"
|
|
139136
|
+
)), paidStore);
|
|
139137
|
+
}
|
|
139051
139138
|
return withRefundNotices(json3(sayFirst({
|
|
139052
139139
|
ok: true,
|
|
139053
139140
|
...paidFields,
|
|
@@ -145157,7 +145244,8 @@ async function runChat({ io, values, deps = {} }) {
|
|
|
145157
145244
|
empty: 0,
|
|
145158
145245
|
pending: void 0,
|
|
145159
145246
|
busy: false,
|
|
145160
|
-
interrupts: 0
|
|
145247
|
+
interrupts: 0,
|
|
145248
|
+
epoch: 0
|
|
145161
145249
|
};
|
|
145162
145250
|
const servedNow = catalogNow;
|
|
145163
145251
|
servedNow();
|
|
@@ -145532,8 +145620,15 @@ ${costLine2(picked.entry.id)}.` + (start ? "" : `
|
|
|
145532
145620
|
The next question uses it${state.mode === "talk" ? ", with the conversation so far" : ""}.`);
|
|
145533
145621
|
};
|
|
145534
145622
|
let reader;
|
|
145623
|
+
const warnPaidPending = (how) => {
|
|
145624
|
+
if (!state.pending || !state.unsure.has(state.pending) || state.dropWarned === state.pending) return false;
|
|
145625
|
+
state.dropWarned = state.pending;
|
|
145626
|
+
say3(`An answer you may have paid for (${usdcWords(state.unsure.get(state.pending))}) is not finished. Type /retry to finish it: it never pays twice. Or ${how}.`);
|
|
145627
|
+
return true;
|
|
145628
|
+
};
|
|
145535
145629
|
const onInterrupt = () => {
|
|
145536
145630
|
if (!state.busy) {
|
|
145631
|
+
if (warnPaidPending("press Ctrl+C again to end the chat and drop it")) return;
|
|
145537
145632
|
reader?.close();
|
|
145538
145633
|
return;
|
|
145539
145634
|
}
|
|
@@ -145746,6 +145841,7 @@ When it says ready, type your message here.`);
|
|
|
145746
145841
|
state.interrupts = 0;
|
|
145747
145842
|
}
|
|
145748
145843
|
state.pending = void 0;
|
|
145844
|
+
const wasUnsure = state.unsure.get(turn);
|
|
145749
145845
|
state.unsure.delete(turn);
|
|
145750
145846
|
const replayed = result.receipt?.replayed === true;
|
|
145751
145847
|
let unpaid = false;
|
|
@@ -145756,6 +145852,7 @@ When it says ready, type your message here.`);
|
|
|
145756
145852
|
}
|
|
145757
145853
|
const paid = unpaid ? 0n : typeof result.paidAtomic === "bigint" ? result.paidAtomic : void 0;
|
|
145758
145854
|
if (paid !== void 0) state.spent += paid;
|
|
145855
|
+
else if (wasUnsure !== void 0 && !unpaid) state.spent += wasUnsure;
|
|
145759
145856
|
const content = typeof result.content === "string" ? result.content : "";
|
|
145760
145857
|
const paidText = unpaid ? "nothing was paid: the earlier payment for this answer never went through" : paid === void 0 ? "paid (the amount was not reported)" : replayed ? `already paid ${usdcWords(paid)} earlier for this answer; nothing new was paid` : `paid ${usdcWords(paid)}`;
|
|
145761
145858
|
const dayRoom2 = dayRoomSentence();
|
|
@@ -145767,7 +145864,7 @@ When it says ready, type your message here.`);
|
|
|
145767
145864
|
return;
|
|
145768
145865
|
}
|
|
145769
145866
|
state.answers += 1;
|
|
145770
|
-
if (state.mode === "talk") state.history = [...turn.messages, { role: "assistant", content }];
|
|
145867
|
+
if (state.mode === "talk" && (turn.epoch ?? 0) === state.epoch) state.history = [...turn.messages, { role: "assistant", content }];
|
|
145771
145868
|
io.out(`${fitColumns(
|
|
145772
145869
|
content,
|
|
145773
145870
|
process.stdout.isTTY ? process.stdout.columns : 0,
|
|
@@ -145788,28 +145885,45 @@ When it says ready, type your message here.`);
|
|
|
145788
145885
|
const failed = async (error, turn) => {
|
|
145789
145886
|
const code2 = errorCodeOf(error);
|
|
145790
145887
|
const surelyNothing = nothingPaid(error);
|
|
145888
|
+
const paidLeft = [error, error?.cause].map((one) => one?.paidLeft).find((one) => one && typeof one === "object" && typeof one.left === "boolean");
|
|
145889
|
+
const leftAtomic = paidLeft?.left === true && /^[1-9][0-9]{0,30}$/.test(String(paidLeft.atomic ?? "")) ? BigInt(paidLeft.atomic) : void 0;
|
|
145791
145890
|
const paidAtomic = error?.details?.receipt ? error.details.paidAtomic : void 0;
|
|
145891
|
+
const wasUnsure = state.unsure.get(turn);
|
|
145792
145892
|
state.pending = void 0;
|
|
145793
145893
|
if (error?.details?.receipt) {
|
|
145894
|
+
state.unsure.delete(turn);
|
|
145794
145895
|
if (typeof paidAtomic === "bigint") state.spent += paidAtomic;
|
|
145896
|
+
else if (wasUnsure !== void 0) state.spent += wasUnsure;
|
|
145795
145897
|
state.empty += 1;
|
|
145796
145898
|
say3(`This answer was paid${typeof paidAtomic === "bigint" ? ` (${usdcWords(paidAtomic)})` : ""}, but no usable answer came back from the AI provider. A new question pays again: ask again, or /model to try another model (${modelIds2}). This chat paid ${paidSoFar()} so far.`);
|
|
145797
145899
|
return;
|
|
145798
145900
|
}
|
|
145799
145901
|
if (code2 === "p03_resource_payment_answer_not_kept") {
|
|
145902
|
+
const earlier = error?.earlierPaymentAtomic ?? error?.cause?.earlierPaymentAtomic;
|
|
145903
|
+
state.unsure.delete(turn);
|
|
145904
|
+
if (/^[1-9][0-9]{0,30}$/.test(String(earlier ?? ""))) state.spent += BigInt(earlier);
|
|
145905
|
+
else if (wasUnsure !== void 0) state.spent += wasUnsure;
|
|
145800
145906
|
state.empty += 1;
|
|
145801
145907
|
say3(`${answerNotKeptWords(error?.earlierPaymentAtomic ?? error?.cause?.earlierPaymentAtomic)} This chat paid ${paidSoFar()} so far.`);
|
|
145802
145908
|
return;
|
|
145803
145909
|
}
|
|
145804
|
-
if (/another_request_unresolved$/.test(code2 ?? "") && (error?.memoryOnlyOpen ?? error?.cause?.memoryOnlyOpen) === true) {
|
|
145910
|
+
if (paidLeft?.left !== true && /another_request_unresolved$/.test(code2 ?? "") && (error?.memoryOnlyOpen ?? error?.cause?.memoryOnlyOpen) === true) {
|
|
145805
145911
|
say3(memoryOnlyOpenWords(error?.closesAtUnix ?? error?.cause?.closesAtUnix));
|
|
145806
145912
|
return;
|
|
145807
145913
|
}
|
|
145808
145914
|
if (code2 === "fetch_proof_tools_unavailable") {
|
|
145915
|
+
const earlier = error?.earlierPaymentAtomic ?? error?.cause?.earlierPaymentAtomic;
|
|
145916
|
+
const heldAtomic = /^[1-9][0-9]{0,30}$/.test(String(earlier ?? "")) ? BigInt(earlier) : leftAtomic ?? wasUnsure;
|
|
145917
|
+
if (heldAtomic !== void 0) {
|
|
145918
|
+
state.unsure.set(turn, heldAtomic);
|
|
145919
|
+
state.pending = turn;
|
|
145920
|
+
say3(`This wallet is missing some setup files, so this try sent nothing and paid nothing new. The earlier payment for this answer (${usdcWords(heldAtomic)}) may be paid already: type /retry to try again (it never pays twice). If you end the chat to run ${commandLine("setup")}, the wallet closes that payment by itself: if it never reached the AI provider, the money comes back.`);
|
|
145921
|
+
return;
|
|
145922
|
+
}
|
|
145809
145923
|
say3(`This wallet is missing some setup files, so nothing was paid. End the chat (/exit), run ${commandLine("setup")} to install them, then start the chat again: ${commandLine("chat")}`);
|
|
145810
145924
|
return;
|
|
145811
145925
|
}
|
|
145812
|
-
if (localErrorCodes(error).includes(SPLIT_CODE)) {
|
|
145926
|
+
if (paidLeft?.left !== true && localErrorCodes(error).includes(SPLIT_CODE)) {
|
|
145813
145927
|
state.pending = turn;
|
|
145814
145928
|
const why = joinOutcome === "stale" ? "the wallet could not read your balance again just now" : joinOutcome === "unfinished" ? "the wallet did not finish getting it ready in this turn" : joinOutcome === "files" ? void 0 : "part of it is not ready to spend yet (another payment of this wallet, or its change, is not final yet)";
|
|
145815
145929
|
say3(why === void 0 ? `Your private balance covers this answer in total, but not yet in one payment. Nothing was paid. Get it ready in another terminal: ${commandLine("prepare")}, then type /retry: it asks the same question again.` : `Your private balance covers this answer in total, but ${why}. Nothing was paid. Type /retry in a minute: it gets the balance ready by itself when it needs to, and never pays twice.`);
|
|
@@ -145861,14 +145975,36 @@ When it says ready, type /retry: it asks the same question again.` : receivedOnl
|
|
|
145861
145975
|
say3(held2 !== void 0 && facts.preparedNotSent === true ? `Nothing was paid: a payment of ${usdcWords(held2)} is prepared for an earlier try of this question and not sent, and the wallet never pays twice for one question. This chat cannot send it. ${closeFirst}` : held2 !== void 0 ? `This question already has a payment of ${usdcWords(held2)} from an earlier try, made with other details, and the wallet never pays twice for one question; nothing more was paid. This chat cannot finish that payment, and the same question typed again is a new question that pays again.` : open4 ? `Nothing was paid: an earlier try of this question was made with other details and is not finished; nothing of it was sent, and it blocks every new question until it is closed. ${closeFirst}` : "Nothing was paid: an earlier try of this question was made with other details. If you still want an answer, type the question again: it goes out as a new question.");
|
|
145862
145976
|
return;
|
|
145863
145977
|
}
|
|
145864
|
-
const float = typeof code2 === "string" && !CAP_CODES.test(code2) ? floatOutcomeWords(code2, facts, chatFloatTerms()) : void 0;
|
|
145978
|
+
const float = typeof code2 === "string" && !CAP_CODES.test(code2) ? floatOutcomeWords(code2, withoutProviderText(facts), chatFloatTerms()) : void 0;
|
|
145865
145979
|
if (float) {
|
|
145866
|
-
const
|
|
145867
|
-
|
|
145868
|
-
|
|
145869
|
-
|
|
145870
|
-
|
|
145871
|
-
|
|
145980
|
+
const keptAmount = () => {
|
|
145981
|
+
try {
|
|
145982
|
+
const kept3 = keptPayment(session.openStore(), turn.requestId);
|
|
145983
|
+
return kept3?.committed === true && kept3.cancelled !== true && /^[1-9][0-9]{0,30}$/.test(String(kept3.amountAtomic ?? "")) ? BigInt(kept3.amountAtomic) : void 0;
|
|
145984
|
+
} catch {
|
|
145985
|
+
return typeof turn.capAtomic === "bigint" && turn.capAtomic > 0n ? turn.capAtomic : void 0;
|
|
145986
|
+
}
|
|
145987
|
+
};
|
|
145988
|
+
const amount = /^[1-9][0-9]{0,30}$/.test(String(facts.paidAtomic ?? "")) ? BigInt(facts.paidAtomic) : paidLeft ? leftAtomic : FLOAT_NOT_PAID.has(code2) ? void 0 : keptAmount();
|
|
145989
|
+
const paidForSure = floatSpent(code2, facts) || code2 === PAID_SELLER_FAILED_CODE;
|
|
145990
|
+
const spent = paidForSure && (amount !== void 0 || wasUnsure !== void 0);
|
|
145991
|
+
if (paidLeft?.left === true && !paidForSure && (FLOAT_NOT_PAID.has(code2) || NOTHING_PAID.test(`${float[1]} ${float[2]}`))) {
|
|
145992
|
+
const held3 = amount ?? leftAtomic ?? (typeof turn.capAtomic === "bigint" && turn.capAtomic > 0n ? turn.capAtomic : void 0);
|
|
145993
|
+
if (held3 !== void 0) state.unsure.set(turn, held3);
|
|
145994
|
+
state.pending = turn;
|
|
145995
|
+
try {
|
|
145996
|
+
(typeof session.timing === "function" ? session.timing() : void 0)?.("chat-answer-failed", code2);
|
|
145997
|
+
} catch {
|
|
145998
|
+
}
|
|
145999
|
+
say3(`The answer did not come back, and the wallet cannot prove this payment${held3 !== void 0 ? ` (${usdcWords(held3)})` : ""} stayed unpaid: it may be paid already. Type /retry to finish it (it never pays twice). This chat counts it until it is certain.`);
|
|
146000
|
+
return;
|
|
146001
|
+
}
|
|
146002
|
+
if (paidForSure) {
|
|
146003
|
+
state.unsure.delete(turn);
|
|
146004
|
+
if (spent) state.spent += amount ?? wasUnsure;
|
|
146005
|
+
} else if (FLOAT_NOT_PAID.has(code2)) state.unsure.delete(turn);
|
|
146006
|
+
else if (amount !== void 0) state.unsure.set(turn, amount);
|
|
146007
|
+
if (paidForSure) state.empty += 1;
|
|
145872
146008
|
if (floatRetry(code2, facts)) state.pending = turn;
|
|
145873
146009
|
try {
|
|
145874
146010
|
const mark = typeof session.timing === "function" ? session.timing() : void 0;
|
|
@@ -145881,17 +146017,33 @@ When it says ready, type /retry: it asks the same question again.` : receivedOnl
|
|
|
145881
146017
|
}
|
|
145882
146018
|
let prepared;
|
|
145883
146019
|
let neverLeft = false;
|
|
145884
|
-
|
|
146020
|
+
let keptAtomic;
|
|
146021
|
+
if (paidLeft) {
|
|
146022
|
+
if (paidLeft.left) keptAtomic = leftAtomic ?? (typeof turn.capAtomic === "bigint" && turn.capAtomic > 0n ? turn.capAtomic : void 0);
|
|
146023
|
+
else {
|
|
146024
|
+
let kept3;
|
|
146025
|
+
try {
|
|
146026
|
+
kept3 = keptPayment(session.openStore(), turn.requestId);
|
|
146027
|
+
} catch {
|
|
146028
|
+
kept3 = void 0;
|
|
146029
|
+
}
|
|
146030
|
+
if (kept3?.prepared === true) prepared = /^[1-9][0-9]{0,30}$/.test(String(kept3.amountAtomic ?? "")) ? ` of ${usdcWords(BigInt(kept3.amountAtomic))}` : "";
|
|
146031
|
+
else neverLeft = true;
|
|
146032
|
+
}
|
|
146033
|
+
} else if (!surelyNothing) {
|
|
145885
146034
|
try {
|
|
145886
146035
|
const kept3 = keptPayment(session.openStore(), turn.requestId);
|
|
146036
|
+
if (/^[1-9][0-9]{0,30}$/.test(String(kept3?.amountAtomic ?? ""))) keptAtomic = BigInt(kept3.amountAtomic);
|
|
145887
146037
|
if (kept3 === void 0 || kept3.committed !== true || kept3.cancelled === true) neverLeft = true;
|
|
145888
|
-
else if (kept3.prepared === true) prepared =
|
|
146038
|
+
else if (kept3.prepared === true) prepared = keptAtomic !== void 0 ? ` of ${usdcWords(keptAtomic)}` : "";
|
|
145889
146039
|
} catch {
|
|
145890
146040
|
prepared = void 0;
|
|
145891
146041
|
neverLeft = false;
|
|
146042
|
+
if (typeof turn.capAtomic === "bigint" && turn.capAtomic > 0n) keptAtomic = turn.capAtomic;
|
|
145892
146043
|
}
|
|
145893
146044
|
}
|
|
145894
|
-
const nothing = surelyNothing || neverLeft;
|
|
146045
|
+
const nothing = paidLeft ? neverLeft : surelyNothing || neverLeft;
|
|
146046
|
+
if (!nothing && prepared === void 0 && keptAtomic !== void 0) state.unsure.set(turn, keptAtomic);
|
|
145895
146047
|
const again = nothing ? "ask again" : prepared !== void 0 ? "type /retry (it sends that same payment, never a second one)" : "type /retry (it finishes the same payment and never pays twice)";
|
|
145896
146048
|
if (!nothing) state.pending = turn;
|
|
145897
146049
|
const paidWords = nothing ? "Nothing was paid." : prepared !== void 0 ? `Nothing was paid: a payment${prepared} is prepared for this question and not sent yet.` : "It may be paid already.";
|
|
@@ -145933,8 +146085,10 @@ When it says ready, type /retry: it asks the same question again.` : receivedOnl
|
|
|
145933
146085
|
const command = async (text3) => {
|
|
145934
146086
|
const [name, ...rest] = text3.split(/\s+/);
|
|
145935
146087
|
const arg = rest.join(" ").trim();
|
|
145936
|
-
if (name === "/exit" || name === "/quit" || name === "/q") return
|
|
146088
|
+
if (name === "/exit" || name === "/quit" || name === "/q") return !warnPaidPending(`${name} again ends the chat and drops it`);
|
|
145937
146089
|
if (name === "/new") {
|
|
146090
|
+
if (warnPaidPending("/new again starts a new conversation and drops it")) return false;
|
|
146091
|
+
state.epoch += 1;
|
|
145938
146092
|
state.history = [];
|
|
145939
146093
|
state.pending = void 0;
|
|
145940
146094
|
forgetMemoryOnly();
|
|
@@ -145948,10 +146102,10 @@ When it says ready, type /retry: it asks the same question again.` : receivedOnl
|
|
|
145948
146102
|
if (!next) say3("Type /mode 1 for one question at a time, or /mode 2 for a conversation.");
|
|
145949
146103
|
else {
|
|
145950
146104
|
state.mode = next;
|
|
146105
|
+
state.epoch += 1;
|
|
145951
146106
|
state.history = [];
|
|
145952
|
-
state.pending
|
|
145953
|
-
|
|
145954
|
-
say3(next === "each" ? "Mode now: one question at a time. Each question goes alone, and the chat names each payment." : "Mode now: a conversation. The AI remembers from your next question on, and the running total shows beside you.");
|
|
146107
|
+
if (!state.pending) forgetMemoryOnly();
|
|
146108
|
+
say3((next === "each" ? "Mode now: one question at a time. Each question goes alone, and the chat names each payment." : "Mode now: a conversation. The AI remembers from your next question on, and the running total shows beside you.") + (state.pending ? " Your unfinished answer is kept: /retry finishes it." : ""));
|
|
145955
146109
|
}
|
|
145956
146110
|
} else if (name === "/model" || name === "/provider") {
|
|
145957
146111
|
const now = await servedNow();
|
|
@@ -146040,6 +146194,7 @@ When it says ready, type /retry: it asks the same question again.` : receivedOnl
|
|
|
146040
146194
|
if (text3.startsWith("/")) {
|
|
146041
146195
|
if (await command(text3)) break;
|
|
146042
146196
|
} else {
|
|
146197
|
+
if (warnPaidPending("type your new question again to drop it and ask the new one")) continue;
|
|
146043
146198
|
if (state.pending) say3("(The unfinished answer is dropped; this is a new question.)");
|
|
146044
146199
|
state.pending = void 0;
|
|
146045
146200
|
const now = await servedNow();
|
|
@@ -146051,12 +146206,16 @@ When it says ready, type /retry: it asks the same question again.` : receivedOnl
|
|
|
146051
146206
|
requestId: `chat-${randomBytes29(8).toString("hex")}`,
|
|
146052
146207
|
model: state.model,
|
|
146053
146208
|
liveEntry: liveOf(state.model),
|
|
146209
|
+
epoch: state.epoch,
|
|
146054
146210
|
messages: state.mode === "each" ? [{ role: "user", content: text3 }] : [...state.history, { role: "user", content: text3 }],
|
|
146055
146211
|
capAtomic: void 0
|
|
146056
146212
|
});
|
|
146057
146213
|
}
|
|
146058
146214
|
}
|
|
146059
146215
|
}
|
|
146216
|
+
if (state.pending && state.unsure.has(state.pending)) {
|
|
146217
|
+
say3(`An answer you may have paid for (${usdcWords(state.unsure.get(state.pending))}) was not finished, and the chat keeps no copy of it. The wallet closes that payment by itself: if it never reached the AI provider, the money comes back to your private balance; if it did, your next private question tells you that it was paid.`);
|
|
146218
|
+
}
|
|
146060
146219
|
say3([
|
|
146061
146220
|
`Chat ended. It paid ${paidSoFar()} for ${questionsWords()}. The conversation is gone from memory.`,
|
|
146062
146221
|
...chatEndLines({ color, columns: process.stderr.columns ?? 0 })
|
|
@@ -146069,7 +146228,7 @@ When it says ready, type /retry: it asks the same question again.` : receivedOnl
|
|
|
146069
146228
|
await session.close();
|
|
146070
146229
|
}
|
|
146071
146230
|
}
|
|
146072
|
-
var SPLIT_CODE, DEFAULT_MODEL2, CHAT_DEFAULT_MODEL, CHAT_MAX_TOKENS, CHAT_SHORT_TOKENS, DEFAULT_ANSWER_CAP_ATOMIC, DEFAULT_CHAT_CAP_ATOMIC, CHEAPEST_MODEL, PICK_PAGE, PICK_MOST_BROWSED, PICK_MATCHES, PICK_COLUMNS, PICK_ID_COLUMNS, PICK_NAME_COLUMNS, STILL_WAITING_MS, higherCap, shortInChat, POSITIVE_ATOMIC9, SHORT_QUESTION, SHORT_QUESTION_TOKENS, PASTE_JOIN_MS, CAP_CODES, WAIT_CODES, NO_ANSWER_CODES, HISTORY_CODES, chatFloatTerms, floatRetry, floatSpent, asSentence, upFirst, lowFirst, NOTHING_PAID, CAP_FROM, PROGRESS_LINE, SHOWN_PROGRESS, isProgressLine, fail46, nothingPaid;
|
|
146231
|
+
var SPLIT_CODE, DEFAULT_MODEL2, CHAT_DEFAULT_MODEL, CHAT_MAX_TOKENS, CHAT_SHORT_TOKENS, DEFAULT_ANSWER_CAP_ATOMIC, DEFAULT_CHAT_CAP_ATOMIC, CHEAPEST_MODEL, PICK_PAGE, PICK_MOST_BROWSED, PICK_MATCHES, PICK_COLUMNS, PICK_ID_COLUMNS, PICK_NAME_COLUMNS, STILL_WAITING_MS, higherCap, shortInChat, POSITIVE_ATOMIC9, SHORT_QUESTION, SHORT_QUESTION_TOKENS, PASTE_JOIN_MS, CAP_CODES, WAIT_CODES, NO_ANSWER_CODES, HISTORY_CODES, chatFloatTerms, floatRetry, floatSpent, FLOAT_NOT_PAID, asSentence, upFirst, lowFirst, NOTHING_PAID, CAP_FROM, PROGRESS_LINE, SHOWN_PROGRESS, isProgressLine, fail46, nothingPaid;
|
|
146073
146232
|
var init_chat = __esm({
|
|
146074
146233
|
"wallet/src/inference/chat.mjs"() {
|
|
146075
146234
|
"use strict";
|
|
@@ -146151,6 +146310,7 @@ var init_chat = __esm({
|
|
|
146151
146310
|
});
|
|
146152
146311
|
floatRetry = (code2, facts) => /_retry_same_request$|_retry_later$/.test(code2) || ["p03_float_already_paid", "p03_float_config_unreadable"].includes(code2) || code2 === "p03_float_request_refused" && facts.floatCode === "float_quote_in_progress" || code2 === "p03_float_seller_kept_payment" && facts.resent !== true && facts.pruned !== true && facts.httpStatus !== 402;
|
|
146153
146312
|
floatSpent = (code2, facts) => code2 === "p03_float_receipt_pruned" || code2 === "p03_float_seller_kept_payment" && !floatRetry(code2, facts);
|
|
146313
|
+
FLOAT_NOT_PAID = /* @__PURE__ */ new Set(["p03_float_request_expired_nothing_paid"]);
|
|
146154
146314
|
asSentence = (text3) => `${text3.charAt(0).toUpperCase()}${text3.slice(1).replace(/[.;\s]+$/, "")}.`;
|
|
146155
146315
|
upFirst = (text3) => `${text3.charAt(0).toUpperCase()}${text3.slice(1)}`;
|
|
146156
146316
|
lowFirst = (text3) => `${text3.charAt(0).toLowerCase()}${text3.slice(1)}`;
|
|
@@ -153398,7 +153558,7 @@ var INFER_MODEL_IDS = ["claude-opus-5", "deepseek-v4-1-flash", "qwen3-8-flash"];
|
|
|
153398
153558
|
var INFER_EXAMPLE_TAIL = `--max-tokens 512 --max-payment 0.02 --max-session 0.1 ${PAYER_SESSION_BASE} --tor`;
|
|
153399
153559
|
var INFER_COST_LINE = "The AI provider charges its quoted cap for --max-tokens a request (often far below 0.01 USDC; 0.01 USDC on its standard offer, so --max-payment must cover that) and keeps what the answer does not use (no refund of the rest); the wallet pays the AI provider's price plus Shinjuku's fee (Shinjuku pays the AI provider for you), never more than --max-payment a request; the AI provider and the company that runs the model see your prompt and the answer. The chain shows one private payment, not who paid";
|
|
153400
153560
|
var INFER_STDIN_MAX_BYTES = 1024 * 1024;
|
|
153401
|
-
var INFER_STDIN_EXAMPLE = `
|
|
153561
|
+
var INFER_STDIN_EXAMPLE = `cat question.txt | ${CLI_WORD} infer -`;
|
|
153402
153562
|
var CURL_USEPOD_NEXT = `use \`${CLI_WORD} infer\` for the AI provider: it sends the prompt only through --tor or --proxy`;
|
|
153403
153563
|
function unshieldArgs(command, args) {
|
|
153404
153564
|
if (args.includes("--amount")) return args;
|
|
@@ -153429,6 +153589,7 @@ function inferArgs(command, args) {
|
|
|
153429
153589
|
index += 1;
|
|
153430
153590
|
}
|
|
153431
153591
|
}
|
|
153592
|
+
if (!out.includes("--prompt") && !out.includes("--help") && !out.includes("-h") && process.stdin.isTTY) out.push("--prompt", "-");
|
|
153432
153593
|
return out;
|
|
153433
153594
|
}
|
|
153434
153595
|
function inferTorGate(env) {
|
|
@@ -153448,7 +153609,7 @@ function validateInfer(values, env) {
|
|
|
153448
153609
|
}
|
|
153449
153610
|
if (values.has("--max-tokens") && Number(values.get("--max-tokens")) < 1) throw new UsageError("--max-tokens must be at least 1 (give 256 or more, 512 answers a short question: a reasoning model like qwen3-8-flash thinks first, and with fewer tokens it can stop before it answers, still paid): it sets the most the request can cost");
|
|
153450
153611
|
if (!values.get("--prompt").trim()) {
|
|
153451
|
-
throw new UsageError(`the prompt is empty:
|
|
153612
|
+
throw new UsageError(`the prompt is empty: run ${CLI_WORD} infer alone in a terminal and type the question when it asks, or pipe it in from a file, like ${INFER_STDIN_EXAMPLE}`);
|
|
153452
153613
|
}
|
|
153453
153614
|
if (!values.has(WALLET_FROM_SETUP) && (values.has("--pool") || values.has("--wallet"))) inferTorGate(env);
|
|
153454
153615
|
}
|
|
@@ -153484,6 +153645,22 @@ async function readStdinBytes({ maxBytes }) {
|
|
|
153484
153645
|
async function inferPrompt(values, deps) {
|
|
153485
153646
|
const given = values.get("--prompt");
|
|
153486
153647
|
if (given !== "-") return given;
|
|
153648
|
+
if (!deps.readStdin && process.stdin.isTTY) {
|
|
153649
|
+
const asker = createInterface2({ input: process.stdin, output: process.stderr, terminal: true });
|
|
153650
|
+
let line;
|
|
153651
|
+
try {
|
|
153652
|
+
line = await new Promise((resolveLine) => {
|
|
153653
|
+
asker.once("close", () => resolveLine(null));
|
|
153654
|
+
asker.question("Type your question, then press Enter: ", resolveLine);
|
|
153655
|
+
});
|
|
153656
|
+
} finally {
|
|
153657
|
+
asker.close();
|
|
153658
|
+
}
|
|
153659
|
+
if (line === null || !String(line).trim()) {
|
|
153660
|
+
throw new UsageError(`no question was typed, so nothing was sent or paid. Run ${cli4()} infer again and type your question, or use ${cli4()} chat`);
|
|
153661
|
+
}
|
|
153662
|
+
return String(line);
|
|
153663
|
+
}
|
|
153487
153664
|
const raw = await (deps.readStdin ?? readStdinBytes)({ maxBytes: INFER_STDIN_MAX_BYTES });
|
|
153488
153665
|
const bytes2 = typeof raw === "string" ? Buffer.from(raw, "utf8") : Buffer.from(raw ?? []);
|
|
153489
153666
|
if (bytes2.length > INFER_STDIN_MAX_BYTES) {
|
|
@@ -153643,10 +153820,13 @@ function inferOutput(result, { values, io, model: model2, quiet, requestId, held
|
|
|
153643
153820
|
` : `paid ${paidText}${quiet ? "" : ` request-id ${result.requestId ?? requestId}`}${stopped}
|
|
153644
153821
|
`);
|
|
153645
153822
|
}
|
|
153823
|
+
const empty = !String(result.content ?? "").trim();
|
|
153824
|
+
const emptyWords = result.finishReason === "length" ? `The model used all its tokens before its answer started, so no answer came back (${replayed || held ? "paid earlier" : `paid ${paidText}`}). Ask again with a higher --max-tokens, or use another model (${cli4()} infer models).` : `The model sent an empty answer (${replayed || held ? "paid earlier" : `paid ${paidText}`}). Ask again: that is a new question and pays again.`;
|
|
153646
153825
|
if (values.has("--json")) {
|
|
153647
153826
|
io.out(`${JSON.stringify(
|
|
153648
153827
|
{
|
|
153649
|
-
ok:
|
|
153828
|
+
ok: !empty,
|
|
153829
|
+
...empty ? { error: "inference_empty_answer", message: emptyWords } : {},
|
|
153650
153830
|
model: result.model ?? model2,
|
|
153651
153831
|
content: result.content,
|
|
153652
153832
|
finishReason: result.finishReason,
|
|
@@ -153661,10 +153841,16 @@ function inferOutput(result, { values, io, model: model2, quiet, requestId, held
|
|
|
153661
153841
|
(key, value2) => typeof value2 === "bigint" ? value2.toString() : value2
|
|
153662
153842
|
)}
|
|
153663
153843
|
`);
|
|
153844
|
+
if (empty) throw new PrintedFailure("inference_empty_answer");
|
|
153664
153845
|
return;
|
|
153665
153846
|
}
|
|
153666
153847
|
for (const line of paidNotices) io.err(`${line}
|
|
153667
153848
|
`);
|
|
153849
|
+
if (empty) {
|
|
153850
|
+
io.err(`${emptyWords}${quiet ? "" : ` request-id ${result.requestId ?? requestId}`}
|
|
153851
|
+
`);
|
|
153852
|
+
throw new PrintedFailure("inference_empty_answer");
|
|
153853
|
+
}
|
|
153668
153854
|
io.out(`${result.content ?? ""}
|
|
153669
153855
|
`);
|
|
153670
153856
|
if (plainPaid) {
|
|
@@ -154243,6 +154429,13 @@ var RanBehindTorFailed = class extends Error {
|
|
|
154243
154429
|
this.exitCode = code2;
|
|
154244
154430
|
}
|
|
154245
154431
|
};
|
|
154432
|
+
var PrintedFailure = class extends Error {
|
|
154433
|
+
constructor(code2) {
|
|
154434
|
+
super(code2);
|
|
154435
|
+
this.code = code2;
|
|
154436
|
+
this.exitCode = 1;
|
|
154437
|
+
}
|
|
154438
|
+
};
|
|
154246
154439
|
function chatBeforeValidate(values, deps, args) {
|
|
154247
154440
|
values.set(CHAT_RAW_ARGS, args);
|
|
154248
154441
|
if (values.has("--wallet") || values.has("--pool")) return void 0;
|
|
@@ -156410,7 +156603,7 @@ var COMMANDS = [
|
|
|
156410
156603
|
summary: "Ask an AI model one question and pay for the answer from your private balance (Shielded Inference, through the AI provider's x402 route).",
|
|
156411
156604
|
env: [],
|
|
156412
156605
|
flags: [
|
|
156413
|
-
flag2("--prompt", "<text>", `what to ask; write - to read it from stdin (
|
|
156606
|
+
flag2("--prompt", "<text>", `what to ask; with no question in a terminal, infer asks for it (the words stay out of your shell history); write - to read it from stdin (a file or a script), or write it alone, in quotes (${CLI_WORD} infer "Summarize x402 in one line" ...)`, { required: true }),
|
|
156414
156607
|
flag2("--model", "<id>", `the model (default ${DEFAULT_MODEL2}): ${INFER_MODEL_IDS.join(", ")} (${CLI_WORD} infer models lists their prices)`),
|
|
156415
156608
|
flag2("--max-tokens", "<n>", "the longest answer to pay for, in tokens; default 4096, 512 for claude-opus-5 (give 256 or more; 512 answers a short question: a reasoning model like qwen3-8-flash thinks first, and with fewer tokens it can stop before it answers, still paid); it sets the most this request can cost", { check: "count" }),
|
|
156416
156609
|
flag2("--max-payment", "<usdc>", `the most this request may pay in all, in USDC (0.02 = 2 cents). It must be at least 0.011: the wallet never sets a call's ceiling below the AI provider's 0.01 USDC floor on its standard offer, and a call also pays Shinjuku's 0.001 USDC fee, so 0.02 covers it. What a call pays is the AI provider's quote for --max-tokens plus Shinjuku's fee: with ${DEFAULT_MODEL2} and the default ${DEFAULT_MAX_TOKENS} tokens, a short question costs about ${usdcOr(DEFAULT_ANSWER_PAID_ATOMIC)}, at most 0.011 USDC: 0.01 USDC plus the fee when the AI provider offers only its standard payment. A higher ceiling is refused before anything is paid. Default: from your limits (setup or limits sets them), else 0.05`, { check: "atomic", usdc: true }),
|
|
@@ -156423,12 +156616,12 @@ var COMMANDS = [
|
|
|
156423
156616
|
],
|
|
156424
156617
|
examples: [
|
|
156425
156618
|
`${CLI_WORD} infer "Say hello in three words"`,
|
|
156426
|
-
|
|
156427
|
-
`
|
|
156619
|
+
`${CLI_WORD} infer`,
|
|
156620
|
+
`cat question.txt | ${CLI_WORD} infer - --model claude-opus-5 --json ${INFER_EXAMPLE_TAIL}`
|
|
156428
156621
|
],
|
|
156429
156622
|
notes: [
|
|
156430
156623
|
`After setup it needs no flags, as chat: config.json gives the wallet, the model is ${DEFAULT_MODEL2}, the caps are from your limits (setup or limits sets them; else 0.05 USDC a call and 1 USDC a run), and it runs behind Tor by itself (Tor must run, as setup recorded it). A flag you give still wins. With --pool or --wallet the command line is the whole config: then add --tor (or --proxy <url>).`,
|
|
156431
|
-
`Private form (recommended):
|
|
156624
|
+
`Private form (recommended): type ${CLI_WORD} infer alone in a terminal, and type the question when it asks: the words are not in the command line, the process list or your shell history. A question typed on the command line (in quotes) is saved in your shell history. For a file or a script, write - and pipe the question in: ${INFER_STDIN_EXAMPLE}; stdin is read until it ends (UTF-8, at most 1 MiB); an empty one is refused before anything is paid.`,
|
|
156432
156625
|
"It runs only through Tor (or --proxy <url>), never a direct connection: the AI provider would see your IP address next to your prompt, so the command refuses before anything is sent.",
|
|
156433
156626
|
`${INFER_COST_LINE}. stderr says this before the payment, with the most the request can cost.`,
|
|
156434
156627
|
"stdout holds the answer; stderr says paid <amount> USDC request-id <id>. An answer cut short by --max-tokens says so. The wallet's own progress lines (Tor, the RPC relay, the pool history walk, the memo reads) show only with --verbose; warnings and failures always show.",
|
|
@@ -156453,7 +156646,7 @@ var COMMANDS = [
|
|
|
156453
156646
|
flags: [bool2("--json", "print the list as one JSON line (prices in atomic USDC per 1M tokens)")],
|
|
156454
156647
|
examples: [`${CLI_WORD} infer models`, `${CLI_WORD} infer models --json`],
|
|
156455
156648
|
notes: [`${INFER_COST_LINE}.`],
|
|
156456
|
-
next: `ask one:
|
|
156649
|
+
next: `ask one: ${CLI_WORD} infer (it asks for the question), or chat: ${CLI_WORD} chat`,
|
|
156457
156650
|
run: (args, values, deps, io) => deps.inferModelsMain(values, { ...deps, io })
|
|
156458
156651
|
},
|
|
156459
156652
|
{
|
|
@@ -159833,7 +160026,7 @@ function failureHint(command, reason, deps, values, floatError) {
|
|
|
159833
160026
|
}
|
|
159834
160027
|
const floatCode = /\b(p03_float_[a-z_]+|wallet_quote_refused_[a-z0-9_]+|p03_resource_paid_seller_failed)\b/.exec(reason)?.[1];
|
|
159835
160028
|
const facts = REQUEST_ID_TAKEN2.test(reason) ? { ...floatError, ...conflictRecordFacts(reason, values, deps) } : floatError;
|
|
159836
|
-
const float = floatCode ? floatOutcomeWords(floatCode, facts, CLI_FLOAT_TERMS(values, deps, command)) : void 0;
|
|
160029
|
+
const float = floatCode ? floatOutcomeWords(floatCode, command?.name === "infer" ? withoutProviderText(facts) : facts, CLI_FLOAT_TERMS(values, deps, command)) : void 0;
|
|
159837
160030
|
if (float) return `${float[1].replace(/[.;\s]+$/, "")}. ${float[2]}`;
|
|
159838
160031
|
const pocketHint = pocketFailureHint(reason, command);
|
|
159839
160032
|
if (pocketHint) return pocketHint;
|
|
@@ -160661,12 +160854,14 @@ This copy: ${walletVersionLine(deps.walletVersion())}
|
|
|
160661
160854
|
}
|
|
160662
160855
|
if (isFamily(name)) {
|
|
160663
160856
|
if (INTERNAL_FAMILIES.has(name) && args.length === 0) throw new UsageError("Unknown command.");
|
|
160664
|
-
|
|
160857
|
+
const aloneInTerminal = args.length === 0 && COMMAND_BY_NAME.has(name) && process.stdin.isTTY === true;
|
|
160858
|
+
if (args.length === 0 && !aloneInTerminal) {
|
|
160665
160859
|
io.out(familyOverviewText(name));
|
|
160666
160860
|
return 0;
|
|
160667
160861
|
}
|
|
160668
160862
|
const three = `${name} ${args[0]} ${args[1]}`;
|
|
160669
|
-
if (
|
|
160863
|
+
if (aloneInTerminal) command = COMMAND_BY_NAME.get(name);
|
|
160864
|
+
else if (COMMAND_BY_NAME.has(name) && !COMMAND_BY_NAME.has(`${name} ${args[0]}`) && !COMMAND_BY_NAME.has(three)) command = COMMAND_BY_NAME.get(name);
|
|
160670
160865
|
else command = COMMAND_BY_NAME.has(three) ? (args.splice(0, 2), COMMAND_BY_NAME.get(three)) : COMMAND_BY_NAME.get(`${name} ${args.shift()}`);
|
|
160671
160866
|
if (!command && INTERNAL_FAMILIES.has(name)) throw new UsageError("Unknown command.");
|
|
160672
160867
|
if (!command) {
|
|
@@ -160742,6 +160937,10 @@ This copy: ${walletVersionLine(deps.walletVersion())}
|
|
|
160742
160937
|
if (endsProcess) showProgressAgain = () => {
|
|
160743
160938
|
};
|
|
160744
160939
|
if (error instanceof RanBehindTorFailed) return error.exitCode;
|
|
160940
|
+
if (error instanceof PrintedFailure) {
|
|
160941
|
+
writeRunResult(deps, command, error.exitCode, error);
|
|
160942
|
+
return error.exitCode;
|
|
160943
|
+
}
|
|
160745
160944
|
if (error instanceof UsageError) {
|
|
160746
160945
|
let setUp = false;
|
|
160747
160946
|
try {
|
|
@@ -160861,7 +161060,7 @@ Next: fund that address, then rerun the same command; it continues where it stop
|
|
|
160861
161060
|
const blockingLine = typeof blocking === "string" && /^[A-Za-z0-9._:-]{1,128}$/.test(blocking) ? `earlier payment: --request-id ${blocking}
|
|
160862
161061
|
` : "";
|
|
160863
161062
|
const payee = command.name === "infer" ? "the AI provider" : "the seller";
|
|
160864
|
-
const sellerSaid = error?.sellerReason ?? error?.cause?.sellerReason;
|
|
161063
|
+
const sellerSaid = command.name === "infer" ? void 0 : error?.sellerReason ?? error?.cause?.sellerReason;
|
|
160865
161064
|
const sellerLine = typeof sellerSaid === "string" && sellerSaid ? `${payee === "the seller" ? "seller" : payee} answered HTTP ${error?.httpStatus ?? error?.cause?.httpStatus}: ${JSON.stringify(sellerSaid)}
|
|
160866
161065
|
` : "";
|
|
160867
161066
|
const landedTx = error?.transaction ?? error?.cause?.transaction;
|