xytara 3.1.13 → 3.1.14

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.
Files changed (55) hide show
  1. package/.env.example +8 -1
  2. package/BSV_TERANODE_PLATFORM_DISCONTINUITY_RUNBOOK.md +126 -126
  3. package/RELEASE_NOTES.md +6 -4
  4. package/lib/a2a_rpc.js +280 -280
  5. package/lib/account_auth.js +585 -585
  6. package/lib/agent_identity.js +162 -162
  7. package/lib/bolt_adapter.js +187 -187
  8. package/lib/bolt_payment_front.js +224 -224
  9. package/lib/checkout_processor_adapter.js +217 -217
  10. package/lib/command_flow.js +722 -722
  11. package/lib/commerce_artifacts.js +591 -591
  12. package/lib/commerce_bridge.js +274 -274
  13. package/lib/commerce_cases.js +399 -399
  14. package/lib/commerce_client.js +7694 -7694
  15. package/lib/commerce_quotes.js +29 -0
  16. package/lib/commerce_runtime.js +322 -322
  17. package/lib/execution_dedupe.js +86 -86
  18. package/lib/external_credit_bridge.js +142 -142
  19. package/lib/http_transport.js +81 -81
  20. package/lib/l402_adapter.js +241 -241
  21. package/lib/l402_payment_front.js +265 -265
  22. package/lib/mpp_adapter.js +237 -237
  23. package/lib/payment_config.js +253 -253
  24. package/lib/payment_verification.js +357 -357
  25. package/lib/provider_payout_execution.js +155 -155
  26. package/lib/provider_registry.js +842 -842
  27. package/lib/rate_limiter.js +81 -81
  28. package/lib/runtime_state_store.js +491 -369
  29. package/lib/settlement_bsv_live.js +600 -600
  30. package/lib/settlement_bsv_teranode.js +117 -117
  31. package/lib/stripe_mpp_payment_front.js +258 -258
  32. package/lib/x402_facilitator.js +191 -191
  33. package/package.json +5 -3
  34. package/scripts/state_backup_offsite.js +196 -0
  35. package/scripts/verify_a2a_rpc.js +105 -105
  36. package/scripts/verify_agent_identity.js +153 -153
  37. package/scripts/verify_all.js +1 -1
  38. package/scripts/verify_bolt_adapter.js +128 -128
  39. package/scripts/verify_cdp_facilitator.js +110 -110
  40. package/scripts/verify_checkout_processor.js +109 -109
  41. package/scripts/verify_commerce_bridge.js +177 -177
  42. package/scripts/verify_execution_dedupe.js +189 -189
  43. package/scripts/verify_execution_surfacing.js +76 -76
  44. package/scripts/verify_final_activation.js +191 -191
  45. package/scripts/verify_l402_adapter.js +86 -86
  46. package/scripts/verify_mpp_adapter.js +128 -128
  47. package/scripts/verify_production_readiness.js +268 -268
  48. package/scripts/verify_rate_limiter.js +52 -52
  49. package/scripts/verify_refund_credit.js +106 -106
  50. package/scripts/verify_release_candidate.js +81 -81
  51. package/scripts/verify_runtime_state_guard.js +117 -0
  52. package/scripts/verify_service.js +15421 -15421
  53. package/scripts/verify_verify_execute_settle.js +208 -208
  54. package/scripts/verify_x402_facilitator.js +286 -286
  55. package/server.js +11976 -11974
@@ -1,208 +1,208 @@
1
- "use strict";
2
-
3
- // Verifies the don't-charge-on-failure guarantee for the x402 command path:
4
- // - when external execution FAILS, the deferred x402 settlement is NEVER called
5
- // (the facilitator /settle is not hit), the response is a non-2xx carrying
6
- // payment_settled:false and a "you were not charged" remediation, and no
7
- // PAYMENT-RESPONSE receipt is emitted;
8
- // - when external execution SUCCEEDS, settlement runs exactly once AFTER the work,
9
- // the response is 200 with the real tool output surfaced and a PAYMENT-RESPONSE
10
- // receipt attached.
11
- //
12
- // This is the verify -> execute -> settle reorder: an x402 authorization is verified
13
- // up front but only settled once the deliverable is produced, so a failed call costs
14
- // the paying agent nothing. No real network or chain is used.
15
-
16
- const assert = require("assert");
17
- const http = require("http");
18
-
19
- const ACCEPTS = [
20
- {
21
- scheme: "exact",
22
- network: "eip155:84532",
23
- asset: "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
24
- amount: "1000",
25
- payTo: "0x7cd2E0Ed2721319B20FAac4109774d030833F84D",
26
- extra: { name: "USDC", version: "2" },
27
- maxTimeoutSeconds: 60
28
- }
29
- ];
30
-
31
- // One mock server stands in for BOTH the x402 facilitator (/verify, /settle) and the
32
- // external execution target (/exec-ok succeeds, /exec-fail rejects with a 502).
33
- function startMockServer() {
34
- const calls = { verify: 0, settle: 0, execOk: 0, execFail: 0 };
35
- const server = http.createServer((req, res) => {
36
- const chunks = [];
37
- req.on("data", (c) => chunks.push(c));
38
- req.on("end", () => {
39
- let body = {};
40
- try { body = JSON.parse(Buffer.concat(chunks).toString("utf8") || "{}"); } catch (_) { body = {}; }
41
- const reply = (payload, status) => {
42
- const text = JSON.stringify(payload);
43
- res.writeHead(status || 200, { "content-type": "application/json", "content-length": Buffer.byteLength(text) });
44
- res.end(text);
45
- };
46
- if (req.method === "POST" && req.url === "/verify") {
47
- calls.verify += 1;
48
- return reply({ isValid: true, payer: "0x1111111111111111111111111111111111111111" });
49
- }
50
- if (req.method === "POST" && req.url === "/settle") {
51
- calls.settle += 1;
52
- const sig = body && body.paymentPayload && body.paymentPayload.payload ? String(body.paymentPayload.payload.signature || "") : "";
53
- if (sig === "settle-fails") return reply({ success: false, errorReason: "insufficient_funds" });
54
- return reply({ success: true, transaction: "0xmocktx", network: "eip155:84532", payer: "0x1111111111111111111111111111111111111111" });
55
- }
56
- if (req.method === "POST" && req.url === "/exec-ok") {
57
- calls.execOk += 1;
58
- return reply({ ok: true, status: "completed", output: { value: "0xdeadbeef" } });
59
- }
60
- if (req.method === "POST" && req.url === "/exec-fail") {
61
- calls.execFail += 1;
62
- return reply({ ok: false, reason: "data_required" }, 502);
63
- }
64
- res.writeHead(404); res.end();
65
- });
66
- });
67
- return new Promise((resolve) => server.listen(0, "127.0.0.1", () => resolve({ server, calls, port: server.address().port })));
68
- }
69
-
70
- function paymentHeader(signature) {
71
- const accepted = ACCEPTS[0];
72
- return Buffer.from(JSON.stringify({
73
- x402Version: 2,
74
- accepted,
75
- payload: {
76
- signature,
77
- authorization: {
78
- from: "0x1111111111111111111111111111111111111111",
79
- to: accepted.payTo,
80
- value: accepted.amount,
81
- validAfter: "0",
82
- validBefore: String(Math.floor(Date.now() / 1000) + 600),
83
- nonce: `0x${"ab".repeat(32)}`
84
- }
85
- }
86
- }), "utf8").toString("base64");
87
- }
88
-
89
- async function post(baseUrl, path, body, headers = {}) {
90
- const res = await fetch(`${baseUrl}${path}`, {
91
- method: "POST",
92
- headers: { "content-type": "application/json", ...headers },
93
- body: JSON.stringify(body)
94
- });
95
- let json = null;
96
- try { json = await res.json(); } catch (_) { json = null; }
97
- return { status: res.status, json, headers: res.headers };
98
- }
99
-
100
- async function main() {
101
- const mock = await startMockServer();
102
- const mockBase = `http://127.0.0.1:${mock.port}`;
103
- const previous = {
104
- mode: process.env.XYTARA_PAYMENT_VERIFICATION_MODE,
105
- accepts: process.env.XYTARA_X402_ACCEPTS_JSON,
106
- facUrl: process.env.XYTARA_X402_FACILITATOR_URL,
107
- targets: process.env.XYTARA_EXECUTION_TARGETS_JSON
108
- };
109
- process.env.XYTARA_PAYMENT_VERIFICATION_MODE = "x402_facilitator";
110
- process.env.XYTARA_X402_ACCEPTS_JSON = JSON.stringify(ACCEPTS);
111
- process.env.XYTARA_X402_FACILITATOR_URL = mockBase;
112
- process.env.XYTARA_EXECUTION_TARGETS_JSON = JSON.stringify([
113
- { target_id: "exec_ok", task_ref: "exec.ok", endpoint_url: `${mockBase}/exec-ok` },
114
- { target_id: "exec_fail", task_ref: "exec.fail", endpoint_url: `${mockBase}/exec-fail` }
115
- ]);
116
-
117
- delete require.cache[require.resolve("../server")];
118
- const { createServer } = require("../server");
119
- const server = createServer();
120
- await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve));
121
- const baseUrl = `http://127.0.0.1:${server.address().port}`;
122
-
123
- try {
124
- // 1. Execution FAILS -> deferred settlement is never reached; caller is not charged.
125
- const settleBeforeFail = mock.calls.settle;
126
- const verifyBeforeFail = mock.calls.verify;
127
- const failBody = { account_id: "acct_ves", command: "failing execution", task_ref: "exec.fail", body: { subject_id: "s-1" } };
128
- const failed = await post(baseUrl, "/x402/commands/execute", failBody, { "payment-signature": paymentHeader("ok") });
129
- assert.strictEqual(failed.status, 502, "failed execution must return 502");
130
- assert.strictEqual(failed.json.ok, false, "failed execution must report ok:false");
131
- assert.strictEqual(failed.json.payment_settled, false, "a failed x402 call must NOT be settled");
132
- assert.ok(/not charged/i.test(String(failed.json.remediation || "")), "failed x402 call must tell the agent it was not charged");
133
- assert.ok(!failed.json.settlement, "failed x402 call must carry no settlement");
134
- assert.strictEqual(failed.headers.get("payment-response"), null, "failed x402 call must NOT emit a PAYMENT-RESPONSE receipt");
135
- assert.strictEqual(mock.calls.settle, settleBeforeFail, "facilitator /settle must NOT be called when execution fails");
136
- assert.strictEqual(mock.calls.verify, verifyBeforeFail + 1, "the payment must still have been verified up front");
137
- assert.strictEqual(mock.calls.execFail, 1, "the failing execution target must have been attempted");
138
-
139
- // 2. Execution SUCCEEDS -> settle runs exactly once, after the work, with a receipt.
140
- const settleBeforeOk = mock.calls.settle;
141
- const okBody = { account_id: "acct_ves", command: "successful execution", task_ref: "exec.ok", body: { subject_id: "s-2" } };
142
- const ok = await post(baseUrl, "/x402/commands/execute", okBody, { "payment-signature": paymentHeader("ok") });
143
- assert.strictEqual(ok.status, 200, "successful execution must return 200");
144
- assert.strictEqual(ok.json.transaction.status, "completed", "successful transaction must be completed");
145
- assert.strictEqual(ok.json.transaction.payment.verification_mode, "x402_facilitator", "verification mode must be recorded");
146
- assert.strictEqual(mock.calls.settle, settleBeforeOk + 1, "facilitator /settle must be called exactly once on success");
147
- assert.strictEqual(mock.calls.execOk, 1, "the successful execution target must have been invoked");
148
- const receiptHeader = ok.headers.get("payment-response");
149
- assert.strictEqual(typeof receiptHeader, "string", "a settled success must carry a PAYMENT-RESPONSE receipt");
150
- const receipt = JSON.parse(Buffer.from(receiptHeader, "base64").toString("utf8"));
151
- assert.strictEqual(receipt.success, true, "settlement receipt must report success");
152
- assert.strictEqual(receipt.transaction, "0xmocktx", "settlement receipt transaction mismatch");
153
-
154
- // 3. Settle FAILS after a SUCCESSFUL execution -> deliverable withheld, not charged.
155
- const settleBeforeSf = mock.calls.settle;
156
- const sfBody = { account_id: "acct_ves", command: "settle fails after success", task_ref: "exec.ok", body: { subject_id: "s-3" } };
157
- const settleFail = await post(baseUrl, "/x402/commands/execute", sfBody, { "payment-signature": paymentHeader("settle-fails") });
158
- assert.strictEqual(settleFail.status, 402, "a failed post-execution settlement must 402");
159
- assert.strictEqual(settleFail.json.ok, false, "a failed settlement must report ok:false");
160
- assert.strictEqual(settleFail.json.payment_settled, false, "a failed settlement must report payment_settled:false");
161
- assert.ok(/not charged/i.test(String(settleFail.json.remediation || "")), "settle-fail must tell the agent it was not charged");
162
- assert.ok(!settleFail.json.transaction, "settle-fail must NOT return the deliverable");
163
- assert.strictEqual(settleFail.headers.get("payment-response"), null, "settle-fail must emit no receipt");
164
- assert.strictEqual(mock.calls.settle, settleBeforeSf + 1, "settle must have been attempted once");
165
- assert.strictEqual(mock.calls.execOk, 2, "the work must have executed before the settle attempt");
166
-
167
- // 4. Non-x402 lane (presence mode, no on-chain settlement) that fails execution must
168
- // NOT claim the caller was charged and must NOT point them at a refund flow — the
169
- // failure flag is keyed on an actual receipt, not merely on "not deferred".
170
- await new Promise((resolve) => server.close(resolve));
171
- const prevMode = process.env.XYTARA_PAYMENT_VERIFICATION_MODE;
172
- process.env.XYTARA_PAYMENT_VERIFICATION_MODE = "presence";
173
- delete require.cache[require.resolve("../server")];
174
- const presenceServerFactory = require("../server").createServer;
175
- const presenceServer = presenceServerFactory();
176
- await new Promise((resolve) => presenceServer.listen(0, "127.0.0.1", resolve));
177
- const presenceBase = `http://127.0.0.1:${presenceServer.address().port}`;
178
- try {
179
- const presFail = await post(presenceBase, "/v1/commands/execute", { account_id: "acct_ves", command: "presence failing exec", task_ref: "exec.fail", body: { subject_id: "s-4" } }, {
180
- "x-wallet-id": "wallet-presence",
181
- "x-payment": "encoded",
182
- "x-payment-signature": "sig"
183
- });
184
- assert.strictEqual(presFail.json.ok, false, "presence failing execution must report ok:false");
185
- assert.strictEqual(presFail.json.payment_settled, false, "presence lane must NOT claim a charge on failure");
186
- assert.ok(!/refund/i.test(String(presFail.json.remediation || "")), "presence lane must NOT point to a refund flow on failure");
187
- assert.strictEqual(presFail.headers.get("payment-response"), null, "presence failure must emit no receipt");
188
- } finally {
189
- await new Promise((resolve) => presenceServer.close(resolve));
190
- if (prevMode === undefined) delete process.env.XYTARA_PAYMENT_VERIFICATION_MODE; else process.env.XYTARA_PAYMENT_VERIFICATION_MODE = prevMode;
191
- }
192
-
193
- console.log("xytara verify:verify-execute-settle passed");
194
- } finally {
195
- await new Promise((resolve) => server.close(resolve));
196
- await new Promise((resolve) => mock.server.close(resolve));
197
- const restore = (key, value) => { if (value === undefined) delete process.env[key]; else process.env[key] = value; };
198
- restore("XYTARA_PAYMENT_VERIFICATION_MODE", previous.mode);
199
- restore("XYTARA_X402_ACCEPTS_JSON", previous.accepts);
200
- restore("XYTARA_X402_FACILITATOR_URL", previous.facUrl);
201
- restore("XYTARA_EXECUTION_TARGETS_JSON", previous.targets);
202
- }
203
- }
204
-
205
- main().catch((error) => {
206
- console.error(error && error.message ? error.message : error);
207
- process.exitCode = 1;
208
- });
1
+ "use strict";
2
+
3
+ // Verifies the don't-charge-on-failure guarantee for the x402 command path:
4
+ // - when external execution FAILS, the deferred x402 settlement is NEVER called
5
+ // (the facilitator /settle is not hit), the response is a non-2xx carrying
6
+ // payment_settled:false and a "you were not charged" remediation, and no
7
+ // PAYMENT-RESPONSE receipt is emitted;
8
+ // - when external execution SUCCEEDS, settlement runs exactly once AFTER the work,
9
+ // the response is 200 with the real tool output surfaced and a PAYMENT-RESPONSE
10
+ // receipt attached.
11
+ //
12
+ // This is the verify -> execute -> settle reorder: an x402 authorization is verified
13
+ // up front but only settled once the deliverable is produced, so a failed call costs
14
+ // the paying agent nothing. No real network or chain is used.
15
+
16
+ const assert = require("assert");
17
+ const http = require("http");
18
+
19
+ const ACCEPTS = [
20
+ {
21
+ scheme: "exact",
22
+ network: "eip155:84532",
23
+ asset: "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
24
+ amount: "1000",
25
+ payTo: "0x7cd2E0Ed2721319B20FAac4109774d030833F84D",
26
+ extra: { name: "USDC", version: "2" },
27
+ maxTimeoutSeconds: 60
28
+ }
29
+ ];
30
+
31
+ // One mock server stands in for BOTH the x402 facilitator (/verify, /settle) and the
32
+ // external execution target (/exec-ok succeeds, /exec-fail rejects with a 502).
33
+ function startMockServer() {
34
+ const calls = { verify: 0, settle: 0, execOk: 0, execFail: 0 };
35
+ const server = http.createServer((req, res) => {
36
+ const chunks = [];
37
+ req.on("data", (c) => chunks.push(c));
38
+ req.on("end", () => {
39
+ let body = {};
40
+ try { body = JSON.parse(Buffer.concat(chunks).toString("utf8") || "{}"); } catch (_) { body = {}; }
41
+ const reply = (payload, status) => {
42
+ const text = JSON.stringify(payload);
43
+ res.writeHead(status || 200, { "content-type": "application/json", "content-length": Buffer.byteLength(text) });
44
+ res.end(text);
45
+ };
46
+ if (req.method === "POST" && req.url === "/verify") {
47
+ calls.verify += 1;
48
+ return reply({ isValid: true, payer: "0x1111111111111111111111111111111111111111" });
49
+ }
50
+ if (req.method === "POST" && req.url === "/settle") {
51
+ calls.settle += 1;
52
+ const sig = body && body.paymentPayload && body.paymentPayload.payload ? String(body.paymentPayload.payload.signature || "") : "";
53
+ if (sig === "settle-fails") return reply({ success: false, errorReason: "insufficient_funds" });
54
+ return reply({ success: true, transaction: "0xmocktx", network: "eip155:84532", payer: "0x1111111111111111111111111111111111111111" });
55
+ }
56
+ if (req.method === "POST" && req.url === "/exec-ok") {
57
+ calls.execOk += 1;
58
+ return reply({ ok: true, status: "completed", output: { value: "0xdeadbeef" } });
59
+ }
60
+ if (req.method === "POST" && req.url === "/exec-fail") {
61
+ calls.execFail += 1;
62
+ return reply({ ok: false, reason: "data_required" }, 502);
63
+ }
64
+ res.writeHead(404); res.end();
65
+ });
66
+ });
67
+ return new Promise((resolve) => server.listen(0, "127.0.0.1", () => resolve({ server, calls, port: server.address().port })));
68
+ }
69
+
70
+ function paymentHeader(signature) {
71
+ const accepted = ACCEPTS[0];
72
+ return Buffer.from(JSON.stringify({
73
+ x402Version: 2,
74
+ accepted,
75
+ payload: {
76
+ signature,
77
+ authorization: {
78
+ from: "0x1111111111111111111111111111111111111111",
79
+ to: accepted.payTo,
80
+ value: accepted.amount,
81
+ validAfter: "0",
82
+ validBefore: String(Math.floor(Date.now() / 1000) + 600),
83
+ nonce: `0x${"ab".repeat(32)}`
84
+ }
85
+ }
86
+ }), "utf8").toString("base64");
87
+ }
88
+
89
+ async function post(baseUrl, path, body, headers = {}) {
90
+ const res = await fetch(`${baseUrl}${path}`, {
91
+ method: "POST",
92
+ headers: { "content-type": "application/json", ...headers },
93
+ body: JSON.stringify(body)
94
+ });
95
+ let json = null;
96
+ try { json = await res.json(); } catch (_) { json = null; }
97
+ return { status: res.status, json, headers: res.headers };
98
+ }
99
+
100
+ async function main() {
101
+ const mock = await startMockServer();
102
+ const mockBase = `http://127.0.0.1:${mock.port}`;
103
+ const previous = {
104
+ mode: process.env.XYTARA_PAYMENT_VERIFICATION_MODE,
105
+ accepts: process.env.XYTARA_X402_ACCEPTS_JSON,
106
+ facUrl: process.env.XYTARA_X402_FACILITATOR_URL,
107
+ targets: process.env.XYTARA_EXECUTION_TARGETS_JSON
108
+ };
109
+ process.env.XYTARA_PAYMENT_VERIFICATION_MODE = "x402_facilitator";
110
+ process.env.XYTARA_X402_ACCEPTS_JSON = JSON.stringify(ACCEPTS);
111
+ process.env.XYTARA_X402_FACILITATOR_URL = mockBase;
112
+ process.env.XYTARA_EXECUTION_TARGETS_JSON = JSON.stringify([
113
+ { target_id: "exec_ok", task_ref: "exec.ok", endpoint_url: `${mockBase}/exec-ok` },
114
+ { target_id: "exec_fail", task_ref: "exec.fail", endpoint_url: `${mockBase}/exec-fail` }
115
+ ]);
116
+
117
+ delete require.cache[require.resolve("../server")];
118
+ const { createServer } = require("../server");
119
+ const server = createServer();
120
+ await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve));
121
+ const baseUrl = `http://127.0.0.1:${server.address().port}`;
122
+
123
+ try {
124
+ // 1. Execution FAILS -> deferred settlement is never reached; caller is not charged.
125
+ const settleBeforeFail = mock.calls.settle;
126
+ const verifyBeforeFail = mock.calls.verify;
127
+ const failBody = { account_id: "acct_ves", command: "failing execution", task_ref: "exec.fail", body: { subject_id: "s-1" } };
128
+ const failed = await post(baseUrl, "/x402/commands/execute", failBody, { "payment-signature": paymentHeader("ok") });
129
+ assert.strictEqual(failed.status, 502, "failed execution must return 502");
130
+ assert.strictEqual(failed.json.ok, false, "failed execution must report ok:false");
131
+ assert.strictEqual(failed.json.payment_settled, false, "a failed x402 call must NOT be settled");
132
+ assert.ok(/not charged/i.test(String(failed.json.remediation || "")), "failed x402 call must tell the agent it was not charged");
133
+ assert.ok(!failed.json.settlement, "failed x402 call must carry no settlement");
134
+ assert.strictEqual(failed.headers.get("payment-response"), null, "failed x402 call must NOT emit a PAYMENT-RESPONSE receipt");
135
+ assert.strictEqual(mock.calls.settle, settleBeforeFail, "facilitator /settle must NOT be called when execution fails");
136
+ assert.strictEqual(mock.calls.verify, verifyBeforeFail + 1, "the payment must still have been verified up front");
137
+ assert.strictEqual(mock.calls.execFail, 1, "the failing execution target must have been attempted");
138
+
139
+ // 2. Execution SUCCEEDS -> settle runs exactly once, after the work, with a receipt.
140
+ const settleBeforeOk = mock.calls.settle;
141
+ const okBody = { account_id: "acct_ves", command: "successful execution", task_ref: "exec.ok", body: { subject_id: "s-2" } };
142
+ const ok = await post(baseUrl, "/x402/commands/execute", okBody, { "payment-signature": paymentHeader("ok") });
143
+ assert.strictEqual(ok.status, 200, "successful execution must return 200");
144
+ assert.strictEqual(ok.json.transaction.status, "completed", "successful transaction must be completed");
145
+ assert.strictEqual(ok.json.transaction.payment.verification_mode, "x402_facilitator", "verification mode must be recorded");
146
+ assert.strictEqual(mock.calls.settle, settleBeforeOk + 1, "facilitator /settle must be called exactly once on success");
147
+ assert.strictEqual(mock.calls.execOk, 1, "the successful execution target must have been invoked");
148
+ const receiptHeader = ok.headers.get("payment-response");
149
+ assert.strictEqual(typeof receiptHeader, "string", "a settled success must carry a PAYMENT-RESPONSE receipt");
150
+ const receipt = JSON.parse(Buffer.from(receiptHeader, "base64").toString("utf8"));
151
+ assert.strictEqual(receipt.success, true, "settlement receipt must report success");
152
+ assert.strictEqual(receipt.transaction, "0xmocktx", "settlement receipt transaction mismatch");
153
+
154
+ // 3. Settle FAILS after a SUCCESSFUL execution -> deliverable withheld, not charged.
155
+ const settleBeforeSf = mock.calls.settle;
156
+ const sfBody = { account_id: "acct_ves", command: "settle fails after success", task_ref: "exec.ok", body: { subject_id: "s-3" } };
157
+ const settleFail = await post(baseUrl, "/x402/commands/execute", sfBody, { "payment-signature": paymentHeader("settle-fails") });
158
+ assert.strictEqual(settleFail.status, 402, "a failed post-execution settlement must 402");
159
+ assert.strictEqual(settleFail.json.ok, false, "a failed settlement must report ok:false");
160
+ assert.strictEqual(settleFail.json.payment_settled, false, "a failed settlement must report payment_settled:false");
161
+ assert.ok(/not charged/i.test(String(settleFail.json.remediation || "")), "settle-fail must tell the agent it was not charged");
162
+ assert.ok(!settleFail.json.transaction, "settle-fail must NOT return the deliverable");
163
+ assert.strictEqual(settleFail.headers.get("payment-response"), null, "settle-fail must emit no receipt");
164
+ assert.strictEqual(mock.calls.settle, settleBeforeSf + 1, "settle must have been attempted once");
165
+ assert.strictEqual(mock.calls.execOk, 2, "the work must have executed before the settle attempt");
166
+
167
+ // 4. Non-x402 lane (presence mode, no on-chain settlement) that fails execution must
168
+ // NOT claim the caller was charged and must NOT point them at a refund flow — the
169
+ // failure flag is keyed on an actual receipt, not merely on "not deferred".
170
+ await new Promise((resolve) => server.close(resolve));
171
+ const prevMode = process.env.XYTARA_PAYMENT_VERIFICATION_MODE;
172
+ process.env.XYTARA_PAYMENT_VERIFICATION_MODE = "presence";
173
+ delete require.cache[require.resolve("../server")];
174
+ const presenceServerFactory = require("../server").createServer;
175
+ const presenceServer = presenceServerFactory();
176
+ await new Promise((resolve) => presenceServer.listen(0, "127.0.0.1", resolve));
177
+ const presenceBase = `http://127.0.0.1:${presenceServer.address().port}`;
178
+ try {
179
+ const presFail = await post(presenceBase, "/v1/commands/execute", { account_id: "acct_ves", command: "presence failing exec", task_ref: "exec.fail", body: { subject_id: "s-4" } }, {
180
+ "x-wallet-id": "wallet-presence",
181
+ "x-payment": "encoded",
182
+ "x-payment-signature": "sig"
183
+ });
184
+ assert.strictEqual(presFail.json.ok, false, "presence failing execution must report ok:false");
185
+ assert.strictEqual(presFail.json.payment_settled, false, "presence lane must NOT claim a charge on failure");
186
+ assert.ok(!/refund/i.test(String(presFail.json.remediation || "")), "presence lane must NOT point to a refund flow on failure");
187
+ assert.strictEqual(presFail.headers.get("payment-response"), null, "presence failure must emit no receipt");
188
+ } finally {
189
+ await new Promise((resolve) => presenceServer.close(resolve));
190
+ if (prevMode === undefined) delete process.env.XYTARA_PAYMENT_VERIFICATION_MODE; else process.env.XYTARA_PAYMENT_VERIFICATION_MODE = prevMode;
191
+ }
192
+
193
+ console.log("xytara verify:verify-execute-settle passed");
194
+ } finally {
195
+ await new Promise((resolve) => server.close(resolve));
196
+ await new Promise((resolve) => mock.server.close(resolve));
197
+ const restore = (key, value) => { if (value === undefined) delete process.env[key]; else process.env[key] = value; };
198
+ restore("XYTARA_PAYMENT_VERIFICATION_MODE", previous.mode);
199
+ restore("XYTARA_X402_ACCEPTS_JSON", previous.accepts);
200
+ restore("XYTARA_X402_FACILITATOR_URL", previous.facUrl);
201
+ restore("XYTARA_EXECUTION_TARGETS_JSON", previous.targets);
202
+ }
203
+ }
204
+
205
+ main().catch((error) => {
206
+ console.error(error && error.message ? error.message : error);
207
+ process.exitCode = 1;
208
+ });