@haven_ai/signer 0.1.21-alpha.0 → 0.1.23-alpha.0

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/dist/cli.cjs CHANGED
@@ -238,9 +238,16 @@ var SUPPORTED_SWEEP_BINDING_VERSIONS = [1];
238
238
  function assertSupportedBindingVersion(received, supported, context) {
239
239
  if (supported.includes(received)) return;
240
240
  const highest = Math.max(...supported);
241
- const ceiling = received > highest ? `This signer is out of date: it supports ${context} versions up to ${highest}, and Haven sent version ${received}. Update @haven_ai/signer \u2014 rerun the Haven connector (\`npx @haven_ai/connect@alpha\`), which reinstalls the pinned MCP runtime.` : `Unsupported ${context} version ${received}: this signer supports ${supported.join(", ")}.`;
242
- throw new sdk.HavenSigningError(
243
- `${ceiling} Nothing was signed. Do not rewrite the version field to a supported value: it is part of the Haven-signed binding message, so changing it invalidates the signature and would misrepresent what Haven authorised.`
241
+ const outOfDate = received > highest;
242
+ const code = context === "x402 expected context" ? sdk.SignerRefusalCode.UnsupportedExpectedContextVersion : sdk.SignerRefusalCode.UnsupportedSweepBindingVersion;
243
+ const ceiling = outOfDate ? `This signer is out of date: it supports ${context} versions up to ${highest}, and Haven sent version ${received}. Update @haven_ai/signer \u2014 rerun the Haven connector (\`npx @haven_ai/connect@alpha\`), which reinstalls the pinned MCP runtime.` : `Unsupported ${context} version ${received}: this signer supports ${supported.join(", ")}.`;
244
+ const fallback = outOfDate ? sdk.SIGNER_UPDATE_FALLBACK : `This ${context} version (${received}) is older than what this signer enforces (${supported.join(", ")}) \u2014 updating @haven_ai/signer will not restore it. Nothing was signed or spent; stop and tell the user rather than retrying.`;
245
+ throw new sdk.HavenUnsupportedSignerVersionError(
246
+ `${ceiling} Nothing was signed. Do not rewrite the version field to a supported value: it is part of the Haven-signed binding message, so changing it invalidates the signature and would misrepresent what Haven authorised.`,
247
+ code,
248
+ supported,
249
+ received,
250
+ fallback
244
251
  );
245
252
  }
246
253
  function assertExpectedBinding(payloadHash, expected, trustedSigner, mode = "hash") {
@@ -339,7 +346,11 @@ function signerInstructions() {
339
346
  "above, this signer is out of date: STOP before signing, and tell the user to update",
340
347
  "@haven_ai/signer by rerunning `npx @haven_ai/connect@alpha`, which reinstalls the pinned",
341
348
  "MCP runtime. Do not edit the version field to a supported value \u2014 it is part of the",
342
- "Haven-signed binding message, so changing it invalidates the signature."
349
+ "Haven-signed binding message, so changing it invalidates the signature.",
350
+ "",
351
+ "A version-mismatch refusal from haven_sign / haven_sign_x402 / haven_sign_sweep_delegate is",
352
+ "machine-readable, not just prose: it carries code, supported_versions, received_version, and",
353
+ "fallback fields alongside the message, so you can branch on it directly."
343
354
  ].join("\n");
344
355
  }
345
356
  async function loadHavenIdentity(credentialsPath) {
@@ -382,11 +393,13 @@ async function fetchX402SignContext(identity, paymentId, fetchImpl = fetch) {
382
393
  "The Haven sign-context response is missing sign_data.typed_data or x402_expected \u2014 the backend may predate #1263. Pass typed_data_b64 from the quote result instead."
383
394
  );
384
395
  }
396
+ const paymentRequired = body.payment_required;
385
397
  return {
386
398
  paymentId: String(body.payment_id ?? paymentId),
387
399
  payloadHash: signData.hash,
388
400
  typedData: signData.typed_data,
389
- x402Expected
401
+ x402Expected,
402
+ paymentRequired: paymentRequired && typeof paymentRequired === "object" && !Array.isArray(paymentRequired) ? paymentRequired : null
390
403
  };
391
404
  }
392
405
 
@@ -485,7 +498,11 @@ var toolSchemas = {
485
498
  // Both optional when payment_id is supplied (the fetch carries them).
486
499
  payload_hash: v3.z.string().regex(/^0x[0-9a-fA-F]+$/, "payload_hash must be a 0x-prefixed hex string").optional(),
487
500
  x402_expected: x402ExpectedSchema.optional(),
488
- payment_required: v3.z.record(v3.z.string(), v3.z.unknown()),
501
+ // #1355: optional when payment_id is supplied — the signer's context fetch
502
+ // carries the stored 402 PaymentRequired, so `{ payment_id }` alone is the
503
+ // preferred call. Still required (via the runtime check in the handler)
504
+ // for the quote-based fallback path without a payment_id.
505
+ payment_required: v3.z.record(v3.z.string(), v3.z.unknown()).optional(),
489
506
  // #1138: delegation-rail intents sign THIS, not payload_hash. Object-typed
490
507
  // (not z.unknown()) so MCP clients embed it as JSON rather than a string.
491
508
  typed_data: v3.z.record(v3.z.string(), v3.z.unknown()).optional(),
@@ -521,8 +538,10 @@ var SIGN_X402_DESCRIPTION = [
521
538
  "One-shot x402 signing for the fast 3-call flow: sign the funding hash AND build the EIP-3009",
522
539
  "X-PAYMENT header in a single local call (equivalent to haven_sign followed by",
523
540
  "haven_x402_sign_header). The delegate key never leaves this process. From the haven_pay_mcp_tool",
524
- "result pass payment_id and payment_required \u2014 PREFERRED (#1263): this signer fetches the exact",
525
- "signing payload and expected context from Haven itself, so nothing bulky crosses your context.",
541
+ "result pass JUST payment_id \u2014 PREFERRED (#1263, #1355): this signer fetches the exact signing",
542
+ "payload, expected context, and merchant payment_required from Haven itself, so nothing bulky",
543
+ "crosses your context. If the signer reports the context carried no payment_required (older",
544
+ "backend), re-call with payment_id plus payment_required verbatim from the pay result.",
526
545
  "Fallback for older backends: pass payload_hash, x402_expected (the nested x402.expected object \u2014",
527
546
  "passing the whole x402 object is also accepted and unwrapped for you), and typed_data_b64",
528
547
  "through UNCHANGED; the signer",
@@ -675,12 +694,18 @@ function createToolHandlers(signer, options = {}) {
675
694
  payloadHash,
676
695
  fetched?.typedData ?? resolveTypedData(args)
677
696
  );
697
+ const paymentRequired = fetched?.paymentRequired ?? args.payment_required;
698
+ if (!paymentRequired) {
699
+ throw new sdk.HavenSigningError(
700
+ fetched ? "The Haven sign-context for this payment_id carried no payment_required (backend predates #1355). Pass payment_required from the pay result explicitly, verbatim." : "payment_required is required on the quote-based path (no payment_id to fetch it by). Pass it verbatim from the quote result."
701
+ );
702
+ }
678
703
  const header = await signer.buildX402PaymentHeader(
679
- args.payment_required,
704
+ paymentRequired,
680
705
  funding.x402Binding
681
706
  );
682
707
  await auditSigning("haven_sign_x402", payloadHash);
683
- await auditSigning("haven_sign_x402", hashPayloadForAudit(args.payment_required));
708
+ await auditSigning("haven_sign_x402", hashPayloadForAudit(paymentRequired));
684
709
  return {
685
710
  signature: funding.signature,
686
711
  x402_binding: funding.x402Binding,
@@ -763,6 +788,17 @@ function normalizeError(err) {
763
788
  statusCode: 400
764
789
  };
765
790
  }
791
+ if (err instanceof sdk.HavenUnsupportedSignerVersionError) {
792
+ return {
793
+ success: false,
794
+ code: err.code,
795
+ message: err.message,
796
+ supported_versions: [...err.supportedVersions],
797
+ received_version: err.receivedVersion,
798
+ fallback: err.fallback,
799
+ next_action: sdk.AgentPaymentNextAction.StopAndTellUser
800
+ };
801
+ }
766
802
  if (err instanceof sdk.HavenSigningError) {
767
803
  return { success: false, code: err.code, message: err.message };
768
804
  }
@@ -998,7 +1034,7 @@ async function warnIfCredentialFilePermissive(path, log = (message) => process.s
998
1034
 
999
1035
  // src/server.ts
1000
1036
  var SIGNER_NAME = "@haven_ai/signer";
1001
- var SIGNER_VERSION = "0.1.21-alpha.0";
1037
+ var SIGNER_VERSION = "0.1.23-alpha.0";
1002
1038
  async function resolveSignerRuntime(options = {}) {
1003
1039
  assertSupportedNodeVersion(options.nodeVersion);
1004
1040
  if (options.delegateKey) {