xytara 3.1.7 → 3.1.9

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 (47) hide show
  1. package/README.md +1230 -1228
  2. package/RELEASE_NOTES.md +6 -8
  3. package/examples/generated.carried-handoff.json +3 -3
  4. package/lib/a2a_rpc.js +280 -0
  5. package/lib/account_auth.js +585 -518
  6. package/lib/agent_identity.js +162 -0
  7. package/lib/bolt_adapter.js +187 -0
  8. package/lib/bolt_payment_front.js +224 -172
  9. package/lib/checkout_processor_adapter.js +217 -0
  10. package/lib/command_flow.js +561 -411
  11. package/lib/commerce_artifacts.js +530 -503
  12. package/lib/commerce_bridge.js +274 -0
  13. package/lib/commerce_cases.js +399 -317
  14. package/lib/commerce_client.js +7694 -7687
  15. package/lib/commerce_runtime.js +322 -315
  16. package/lib/execution_dedupe.js +86 -0
  17. package/lib/external_credit_bridge.js +142 -142
  18. package/lib/external_execution_runtime.js +82 -34
  19. package/lib/http_transport.js +81 -73
  20. package/lib/l402_adapter.js +241 -0
  21. package/lib/l402_payment_front.js +265 -192
  22. package/lib/mpp_adapter.js +237 -0
  23. package/lib/payment_verification.js +52 -9
  24. package/lib/provider_network_contract.js +143 -0
  25. package/lib/provider_payout_execution.js +137 -0
  26. package/lib/provider_registry.js +666 -0
  27. package/lib/rate_limiter.js +81 -0
  28. package/lib/runtime_state_store.js +369 -354
  29. package/lib/stripe_mpp_payment_front.js +258 -158
  30. package/package.json +18 -3
  31. package/scripts/verify_a2a_rpc.js +105 -0
  32. package/scripts/verify_agent_identity.js +153 -0
  33. package/scripts/verify_bolt_adapter.js +128 -0
  34. package/scripts/verify_checkout_processor.js +109 -0
  35. package/scripts/verify_commerce_bridge.js +177 -0
  36. package/scripts/verify_execution_dedupe.js +189 -0
  37. package/scripts/verify_execution_surfacing.js +76 -0
  38. package/scripts/verify_external_execution_retry.js +113 -0
  39. package/scripts/verify_l402_adapter.js +86 -0
  40. package/scripts/verify_mpp_adapter.js +128 -0
  41. package/scripts/verify_provider_network.js +567 -0
  42. package/scripts/verify_rate_limiter.js +52 -0
  43. package/scripts/verify_refund_credit.js +106 -0
  44. package/scripts/verify_service.js +15421 -15410
  45. package/scripts/verify_settlement.js +116 -0
  46. package/scripts/verify_verify_execute_settle.js +208 -0
  47. package/server.js +684 -16
package/RELEASE_NOTES.md CHANGED
@@ -1,11 +1,9 @@
1
- `xytara` 3.1.7 completes the Coinbase CDP facilitator path, proven with a real Base mainnet settlement.
2
-
3
- A facilitator can signal a verification or settlement result either with a 200 and the result in the body, or with a non-2xx status and the same body. Coinbase CDP returns HTTP 400 for an invalid payment (with `{isValid:false, invalidReason, invalidMessage}`). The client previously treated any non-2xx as an unreachable transport error and never read the body, so a CDP verdict was misclassified. This release reads the result from the body regardless of HTTP status, which was the last blocker for Base mainnet settlement through CDP.
1
+ `xytara` 3.1.9 adds the open execution-provider network: the runtime can now route paid agent calls to independently-registered providers, split the settlement, and reconcile payouts.
4
2
 
5
3
  Highlights:
6
4
 
7
- - the facilitator client treats any `/verify` or `/settle` response that carries an `isValid` or `success` flag as the facilitator's result, regardless of HTTP status; only a missing or unparseable body is a transport failure
8
- - failure details now surface the facilitator's `invalidReason` / `invalidMessage` / `errorReason` and a truncated response-body excerpt, so operators can see exactly why a facilitator rejected a payment
9
- - adds regression checks for facilitators that return a non-2xx status with a result body
10
- - no change for facilitators that return 200 with the result; the default Base Sepolia path is unaffected
11
- - verified end to end: a real Base mainnet USDC payment settled on-chain through the CDP facilitator and minted self-serve credits
5
+ - **Open provider network** — self-registration (`POST /v1/provider-network/register`, operator-activated or permissionless + rate-limited), capability matching, public discovery, and a machine-readable integration contract (`GET /v1/provider-network/contract`).
6
+ - **Routing resilience** — per-provider health with cooldown/half-open, in-request failover on provably-not-processed failures only (no double-execution), and reputation-weighted ordering (smoothed success rate).
7
+ - **Economics** — floored revenue-share split (platform keeps the remainder), a reconciled, non-custodial payout ledger (create → on-chain send → confirm), and optional payout auto-execution via an external signer (the runtime stays key-less).
8
+ - **Agent identity** — spend-auth proofs can bind to the exact account/amount/task (request binding), with an optional strict mode.
9
+ - Back-compatible: an empty registry is a byte-for-byte no-op, and env-pinned execution targets still resolve first.
@@ -6,9 +6,9 @@
6
6
  "source_ref": "spine.job.commitment",
7
7
  "committed_ref": "transaction.job.commitment",
8
8
  "precommit_pack_ref": "precommit.job.commitment.readiness",
9
- "transaction_id": "txn_2b8e48010d89",
10
- "receipt_id": "rcpt_5b59fa10c38d",
11
- "proof_ref": "proof_rcpt_5b59fa10c38d",
9
+ "transaction_id": "txn_9ee101eb66d3",
10
+ "receipt_id": "rcpt_b89c5a8c80f0",
11
+ "proof_ref": "proof_rcpt_b89c5a8c80f0",
12
12
  "payment_protocol": "x402",
13
13
  "settlement_mode": "evm_payment",
14
14
  "treasury_destination_id": "treasury_acct_demo",
package/lib/a2a_rpc.js ADDED
@@ -0,0 +1,280 @@
1
+ "use strict";
2
+
3
+ // A2A JSON-RPC endpoint with the x402 payment extension (in-band).
4
+ //
5
+ // Spec shape (A2A + x402 extension): payment travels inside the A2A message
6
+ // metadata, not an HTTP header. An unpaid `message/send` returns a Task in
7
+ // state `input-required` whose status message metadata carries
8
+ // `x402.payment.status: "payment-required"` and `x402.payment.required` (the
9
+ // advertised x402 accepts). The caller re-sends `message/send` with the signed
10
+ // x402 authorization in `x402.payment.payload`; we verify + settle it through
11
+ // the same proven facilitator path used by the HTTP x402 routes, run the
12
+ // requested task, and return a `completed` Task carrying the settlement in
13
+ // `x402.payment.receipts` (plus a PAYMENT-RESPONSE header for header-aware
14
+ // clients).
15
+ //
16
+ // This module is dependency-injected so it can be unit-tested without a live
17
+ // facilitator or network: see verify.js.
18
+
19
+ const crypto = require("crypto");
20
+
21
+ // x402 A2A extension URI (declared in the agent card capabilities.extensions).
22
+ const X402_EXTENSION_URI = "https://github.com/google-a2a/a2a-x402/tree/main/spec/v0.1";
23
+
24
+ // Metadata keys defined by the x402 A2A extension.
25
+ const META_STATUS = "x402.payment.status";
26
+ const META_REQUIRED = "x402.payment.required";
27
+ const META_PAYLOAD = "x402.payment.payload";
28
+ const META_RECEIPTS = "x402.payment.receipts";
29
+ const META_ERROR = "x402.payment.error";
30
+
31
+ function uuid() {
32
+ return crypto.randomUUID();
33
+ }
34
+
35
+ function jsonrpcResult(id, result) {
36
+ return { jsonrpc: "2.0", id: typeof id === "undefined" ? null : id, result };
37
+ }
38
+
39
+ function jsonrpcError(id, code, message, data) {
40
+ return {
41
+ jsonrpc: "2.0",
42
+ id: typeof id === "undefined" ? null : id,
43
+ error: { code, message, ...(typeof data === "undefined" ? {} : { data }) }
44
+ };
45
+ }
46
+
47
+ function agentMessage(text, metadata) {
48
+ return {
49
+ role: "agent",
50
+ kind: "message",
51
+ messageId: uuid(),
52
+ parts: [{ kind: "text", text }],
53
+ ...(metadata ? { metadata } : {})
54
+ };
55
+ }
56
+
57
+ function paymentRequiredTask({ taskId, contextId, accepts, skillTaskRef }) {
58
+ return {
59
+ id: taskId,
60
+ contextId,
61
+ kind: "task",
62
+ status: {
63
+ state: "input-required",
64
+ message: agentMessage(
65
+ `Payment required to run "${skillTaskRef}". Re-send message/send with the signed x402 authorization in metadata["${META_PAYLOAD}"].`,
66
+ {
67
+ [META_STATUS]: "payment-required",
68
+ [META_REQUIRED]: { x402Version: 2, scheme: "exact", accepts }
69
+ }
70
+ ),
71
+ timestamp: new Date().toISOString()
72
+ },
73
+ artifacts: [],
74
+ history: []
75
+ };
76
+ }
77
+
78
+ function failedTask({ taskId, contextId, paymentStatus, error }) {
79
+ return {
80
+ id: taskId,
81
+ contextId,
82
+ kind: "task",
83
+ status: {
84
+ state: "failed",
85
+ message: agentMessage(`Payment ${paymentStatus}: ${error || "error"}`, {
86
+ [META_STATUS]: paymentStatus,
87
+ [META_ERROR]: error || null
88
+ }),
89
+ timestamp: new Date().toISOString()
90
+ },
91
+ artifacts: [],
92
+ history: []
93
+ };
94
+ }
95
+
96
+ function completedTask({ taskId, contextId, resultPayload, settlement }) {
97
+ return {
98
+ id: taskId,
99
+ contextId,
100
+ kind: "task",
101
+ status: {
102
+ state: "completed",
103
+ message: agentMessage("Completed and settled.", {
104
+ [META_STATUS]: "payment-completed",
105
+ [META_RECEIPTS]: [settlement]
106
+ }),
107
+ timestamp: new Date().toISOString()
108
+ },
109
+ artifacts: [
110
+ {
111
+ artifactId: uuid(),
112
+ name: "result",
113
+ parts: [{ kind: "data", data: resultPayload }]
114
+ }
115
+ ],
116
+ history: []
117
+ };
118
+ }
119
+
120
+ // Pull the task reference + body an agent wants to run out of the A2A message.
121
+ // Accepts an explicit `task_ref`/`skill_id`/`skill` in message.metadata, plus a
122
+ // params/body object (metadata.params | metadata.body | a DataPart's data).
123
+ function resolveSkill(message) {
124
+ const meta = (message && message.metadata) || {};
125
+ const skillTaskRef = String(meta.task_ref || meta.skill_id || meta.skill || "a2a.negotiate");
126
+ let body = meta.params || meta.body || null;
127
+ if (!body && message && Array.isArray(message.parts)) {
128
+ const dataPart = message.parts.find((p) => p && p.kind === "data" && p.data && typeof p.data === "object");
129
+ if (dataPart) body = dataPart.data;
130
+ }
131
+ return { skillTaskRef, body: body && typeof body === "object" ? body : {} };
132
+ }
133
+
134
+ // Handle a single `message/send` call. Returns { http, headers, body }.
135
+ // `ctx` = { config, resourceUrl, deps } where deps has the injected runtime
136
+ // functions (facilitator factory, verify/settle, receipt encoder, authorized
137
+ // executor, task-payload builder, and `state`).
138
+ async function handleMessageSend(rpc, ctx) {
139
+ const id = rpc.id;
140
+ const params = rpc.params || {};
141
+ const message = params.message || {};
142
+ const meta = message.metadata || {};
143
+
144
+ const config = ctx.config || {};
145
+ const x402 = config.x402 || {};
146
+ const accepts = Array.isArray(x402.accepts) ? x402.accepts : [];
147
+
148
+ if (config.verification_mode !== "x402_facilitator") {
149
+ return { http: 200, body: jsonrpcError(id, -32010, "x402 settlement is not enabled on this runtime") };
150
+ }
151
+ if (!accepts.length) {
152
+ return { http: 200, body: jsonrpcError(id, -32011, "x402 accepts are not configured") };
153
+ }
154
+
155
+ const taskId = uuid();
156
+ const contextId = message.contextId || message.taskId || uuid();
157
+ const { skillTaskRef, body } = resolveSkill(message);
158
+
159
+ const payload = meta[META_PAYLOAD];
160
+ if (!payload) {
161
+ // In-band 402: Task(input-required) carrying the x402 requirements.
162
+ return {
163
+ http: 200,
164
+ body: jsonrpcResult(id, paymentRequiredTask({ taskId, contextId, accepts, skillTaskRef }))
165
+ };
166
+ }
167
+
168
+ // Payment present: verify + settle via the same facilitator path as the HTTP routes.
169
+ const facilitator = ctx.deps.createFacilitator({
170
+ url: x402.facilitator_url,
171
+ headers: x402.facilitator_headers,
172
+ timeoutMs: x402.facilitator_timeout_ms,
173
+ cdp: x402.cdp
174
+ });
175
+ const paymentHeader =
176
+ typeof payload === "string" ? payload : Buffer.from(JSON.stringify(payload), "utf8").toString("base64");
177
+
178
+ const auth = await ctx.deps.verifyX402Payment({
179
+ paymentHeader,
180
+ accepts,
181
+ resourceUrl: ctx.resourceUrl || null,
182
+ facilitator
183
+ });
184
+ if (!auth.ok) {
185
+ return {
186
+ http: 200,
187
+ body: jsonrpcResult(id, failedTask({ taskId, contextId, paymentStatus: "payment-rejected", error: auth.detail || auth.reason }))
188
+ };
189
+ }
190
+
191
+ const settled = await ctx.deps.settleX402Payment({ authorization: auth.authorization, facilitator, settle: x402.settle });
192
+ if (!settled.ok) {
193
+ return {
194
+ http: 200,
195
+ body: jsonrpcResult(id, failedTask({ taskId, contextId, paymentStatus: "payment-failed", error: settled.detail || settled.reason }))
196
+ };
197
+ }
198
+
199
+ const requirements = (auth.authorization && auth.authorization.paymentRequirements) || {};
200
+ const requestPayload = {
201
+ ...ctx.deps.buildNamedTaskPayload(skillTaskRef, body, skillTaskRef),
202
+ payment_protocol: "x402"
203
+ };
204
+ const authorizationContext = {
205
+ verification_mode: "x402_facilitator",
206
+ wallet_id: (auth.authorization && auth.authorization.payer) || "x402_payer",
207
+ payment_payload: {
208
+ x402_payer: (auth.authorization && auth.authorization.payer) || null,
209
+ x402_network: requirements.network || null,
210
+ x402_asset: requirements.asset || null,
211
+ x402_amount: requirements.amount || null
212
+ }
213
+ };
214
+
215
+ const exec = await ctx.deps.executeAuthorized(ctx.deps.state, requestPayload, authorizationContext);
216
+ const receipt = ctx.deps.encodeReceipt(settled.settlement);
217
+ const execOk = Boolean(exec) && exec.status === 200 && (!exec.payload || exec.payload.ok !== false);
218
+ if (!execOk) {
219
+ // The payment settled (money moved) but the skill did not execute. Report
220
+ // that honestly as a failed task while still carrying the settlement receipt,
221
+ // so the paying agent knows it paid and can pursue a refund/retry.
222
+ const failed = {
223
+ id: taskId,
224
+ contextId,
225
+ kind: "task",
226
+ status: {
227
+ state: "failed",
228
+ message: agentMessage("Payment settled, but the requested skill failed to execute.", {
229
+ [META_STATUS]: "payment-completed",
230
+ [META_RECEIPTS]: [settled.settlement],
231
+ execution_error: (exec && exec.payload) || "execution_failed",
232
+ execution_status: exec && exec.status
233
+ }),
234
+ timestamp: new Date().toISOString()
235
+ },
236
+ artifacts: [],
237
+ history: []
238
+ };
239
+ return {
240
+ http: 200,
241
+ headers: { "PAYMENT-RESPONSE": receipt, "Cache-Control": "no-store" },
242
+ body: jsonrpcResult(id, failed)
243
+ };
244
+ }
245
+ const task = completedTask({ taskId, contextId, resultPayload: exec.payload, settlement: settled.settlement });
246
+ return {
247
+ http: 200,
248
+ headers: { "PAYMENT-RESPONSE": receipt, "Cache-Control": "no-store" },
249
+ body: jsonrpcResult(id, task)
250
+ };
251
+ }
252
+
253
+ // Top-level JSON-RPC dispatcher for POST /v1/a2a.
254
+ async function handleA2ARpc(rpc, ctx) {
255
+ if (!rpc || typeof rpc !== "object" || Array.isArray(rpc)) {
256
+ return { http: 200, body: jsonrpcError(null, -32600, "Invalid Request: expected a single JSON-RPC 2.0 object") };
257
+ }
258
+ if (rpc.jsonrpc !== "2.0") {
259
+ return { http: 200, body: jsonrpcError(rpc.id, -32600, "Invalid Request: jsonrpc must be \"2.0\"") };
260
+ }
261
+ if (rpc.method === "message/send") {
262
+ return handleMessageSend(rpc, ctx);
263
+ }
264
+ if (rpc.method === "message/stream") {
265
+ return { http: 200, body: jsonrpcError(rpc.id, -32004, "Streaming is not supported by this agent (capabilities.streaming=false)") };
266
+ }
267
+ return { http: 200, body: jsonrpcError(rpc.id, -32601, `Method not found: ${rpc.method}`) };
268
+ }
269
+
270
+ module.exports = {
271
+ handleA2ARpc,
272
+ handleMessageSend,
273
+ resolveSkill,
274
+ X402_EXTENSION_URI,
275
+ META_STATUS,
276
+ META_REQUIRED,
277
+ META_PAYLOAD,
278
+ META_RECEIPTS,
279
+ META_ERROR
280
+ };