warpmetal 0.7.8 → 0.7.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.
package/README.md CHANGED
@@ -124,6 +124,12 @@ treasury; the merchant tenant's active allowance controls sponsorship
124
124
  admission but is not charged actual gas. Payment authority depends on execution
125
125
  context:
126
126
 
127
+ WarpMetal can advertise any currently executable subset of those rails, from a
128
+ single alternative through all three. Agent Kit requires the sponsorship and
129
+ external-recipient extension bindings to match the advertised alternatives
130
+ exactly, then preserves the chosen authorization artifact byte-for-byte for
131
+ every pending or ambiguous retry.
132
+
127
133
  - When a human is actively chatting with the agent, show the exact live terms
128
134
  and ask for confirmation immediately before authorizing and submitting.
129
135
  - In an unattended run, a pre-funded dedicated wallet is standing spend
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "warpmetal",
3
- "version": "0.7.8",
3
+ "version": "0.7.9",
4
4
  "description": "Agent-safe CLI and skill for purchasing, renewing, and managing WarpMetal VPS servers",
5
5
  "type": "module",
6
6
  "bin": {
package/src/payment.js CHANGED
@@ -338,6 +338,47 @@ function externalRecipientBinds(requirement, extensions) {
338
338
  return matches.length === 1;
339
339
  }
340
340
 
341
+ function exactExtensionBindings(accepts, sponsorship, extensions) {
342
+ if (!sponsorship) return;
343
+ const sponsoredAccepts = new Set(
344
+ accepts.map(
345
+ (requirement) =>
346
+ `${requirement.network}|${requirement.asset.toLowerCase()}|${requirement.extra.payloadProfile}`,
347
+ ),
348
+ );
349
+ const sponsorshipBindings = new Set(
350
+ sponsorship.requirements.map(
351
+ (binding) =>
352
+ `${binding.network}|${binding.asset.toLowerCase()}|${binding.payloadProfile}`,
353
+ ),
354
+ );
355
+ const recipients = extensions["com.k1hub.external-recipient"]?.info?.recipients;
356
+ const acceptedRecipients = new Set(
357
+ accepts.map(
358
+ (requirement) =>
359
+ `${requirement.network}|${requirement.asset}|${requirement.payTo}`,
360
+ ),
361
+ );
362
+ const recipientBindings = new Set(
363
+ Array.isArray(recipients)
364
+ ? recipients.map(
365
+ (recipient) =>
366
+ `${recipient.network}|${recipient.asset}|${recipient.payTo}`,
367
+ )
368
+ : [],
369
+ );
370
+ if (
371
+ sponsoredAccepts.size !== sponsorshipBindings.size ||
372
+ [...sponsoredAccepts].some((identity) => !sponsorshipBindings.has(identity)) ||
373
+ acceptedRecipients.size !== recipientBindings.size ||
374
+ [...acceptedRecipients].some((identity) => !recipientBindings.has(identity))
375
+ ) {
376
+ throw invalid(
377
+ "The x402 sponsorship and recipient bindings do not exactly match PAYMENT-REQUIRED.",
378
+ );
379
+ }
380
+ }
381
+
341
382
  function agentWalletSupports(requirement, sponsorship, extensions) {
342
383
  const profile = requirement.extra.payloadProfile;
343
384
  const baseExtra = Object.keys(requirement.extra).sort().join(",");
@@ -397,6 +438,7 @@ export function parsePaymentRequired(value, expectedUrl) {
397
438
  "The x402 challenge extension",
398
439
  );
399
440
  const sponsorship = extensions[GAS_SPONSORSHIP_EXTENSION]?.info;
441
+ exactExtensionBindings(accepts, sponsorship, extensions);
400
442
  return {
401
443
  challenge: parsed,
402
444
  paymentRequiredDigest: digestJson(parsed),