@haven_ai/signer 0.0.0-dev.202609250737.3bd5a51 → 0.0.0-dev.202609251409.98ed67a

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/index.cjs CHANGED
@@ -431,6 +431,12 @@ var HavenSignContextError = class extends edge.HavenSigningError {
431
431
  http_status;
432
432
  /** Present only for `SIGN_CONTEXT_REFUSED`: the backend's own `error_code`. */
433
433
  backend_error_code;
434
+ /**
435
+ * #3303: on a 426 `client_outdated` refusal, the backend's update hint —
436
+ * this signer is below the minimum the deployment set, and
437
+ * `client_update.upgrade_command` is what updates it.
438
+ */
439
+ client_update;
434
440
  constructor(message, code, refusal, paymentId, flow = "x402") {
435
441
  super(message);
436
442
  this.code = code;
@@ -439,7 +445,15 @@ var HavenSignContextError = class extends edge.HavenSigningError {
439
445
  if (code === "SIGN_CONTEXT_REFUSED") {
440
446
  this.http_status = refusal?.httpStatus;
441
447
  this.backend_error_code = refusal?.errorCode;
442
- if (flow === "direct" && refusal?.httpStatus === 404) {
448
+ if (refusal?.errorCode === "client_outdated") {
449
+ this.client_update = refusal.clientUpdate;
450
+ this.next_action = edge.AgentPaymentNextAction.StopAndTellUser;
451
+ step = signerRefusalStep({
452
+ nextAction: edge.AgentPaymentNextAction.StopAndTellUser,
453
+ nextTool: null,
454
+ nextToolOmittedReason: `this signer is below the minimum version Haven accepts \u2014 tell the user to run ${refusal.clientUpdate?.upgrade_command ?? "the connector again"}, restart the agent runtime, then retry the same haven_sign / haven_sign_x402 call`
455
+ });
456
+ } else if (flow === "direct" && refusal?.httpStatus === 404) {
443
457
  this.fallback = "typed_data_b64";
444
458
  this.next_action = edge.AgentPaymentNextAction.StopAndTellUser;
445
459
  step = signerRefusalStep({
@@ -497,13 +511,26 @@ async function loadHavenIdentity(credentialsPath) {
497
511
  }
498
512
  }
499
513
  var SIGN_CONTEXT_TIMEOUT_MS = 15e3;
500
- async function fetchX402SignContext(identity, paymentId, fetchImpl = fetch, timeoutMs = SIGN_CONTEXT_TIMEOUT_MS) {
514
+ function signContextHeaders(identity, clientIdentity) {
515
+ return {
516
+ Authorization: `Bearer ${identity.apiKey}`,
517
+ ...clientIdentity ? { [edge.HAVEN_CLIENT_HEADER]: clientIdentity } : {}
518
+ };
519
+ }
520
+ function refusalOf(status, body) {
521
+ return {
522
+ httpStatus: status,
523
+ errorCode: typeof body.error_code === "string" ? body.error_code : void 0,
524
+ clientUpdate: edge.readClientUpdate(body.client_update)
525
+ };
526
+ }
527
+ async function fetchX402SignContext(identity, paymentId, fetchImpl = fetch, timeoutMs = SIGN_CONTEXT_TIMEOUT_MS, clientIdentity) {
501
528
  let response;
502
529
  try {
503
530
  response = await fetchImpl(
504
531
  `${identity.apiUrl}/x402/${encodeURIComponent(paymentId)}/sign-context`,
505
532
  {
506
- headers: { Authorization: `Bearer ${identity.apiKey}` },
533
+ headers: signContextHeaders(identity, clientIdentity),
507
534
  signal: AbortSignal.timeout(timeoutMs)
508
535
  }
509
536
  );
@@ -535,7 +562,7 @@ async function fetchX402SignContext(identity, paymentId, fetchImpl = fetch, time
535
562
  throw new HavenSignContextError(
536
563
  `Haven refused the signing-context fetch for ${paymentId}: ${detail}` + (response.status === 404 ? " \u2014 check the payment_id came from this agent\u2019s own quote." : response.status === 410 ? " Re-run the quote with the same idempotency key, then sign the fresh payment_id." : ""),
537
564
  "SIGN_CONTEXT_REFUSED",
538
- { httpStatus: response.status, errorCode: typeof body.error_code === "string" ? body.error_code : void 0 },
565
+ refusalOf(response.status, body),
539
566
  paymentId
540
567
  );
541
568
  }
@@ -555,17 +582,18 @@ async function fetchX402SignContext(identity, paymentId, fetchImpl = fetch, time
555
582
  payloadHash: signData.hash,
556
583
  typedData: signData.typed_data,
557
584
  x402Expected,
558
- paymentRequired: paymentRequired && typeof paymentRequired === "object" && !Array.isArray(paymentRequired) ? paymentRequired : null
585
+ paymentRequired: paymentRequired && typeof paymentRequired === "object" && !Array.isArray(paymentRequired) ? paymentRequired : null,
586
+ clientUpdate: edge.readClientUpdate(body.client_update)
559
587
  };
560
588
  }
561
589
  var SUPPORTED_DIRECT_SIGN_CONTEXT_VERSIONS = [edge.DIRECT_SIGN_CONTEXT_VERSION];
562
- async function fetchDirectSignContext(identity, paymentId, fetchImpl = fetch, timeoutMs = SIGN_CONTEXT_TIMEOUT_MS) {
590
+ async function fetchDirectSignContext(identity, paymentId, fetchImpl = fetch, timeoutMs = SIGN_CONTEXT_TIMEOUT_MS, clientIdentity) {
563
591
  let response;
564
592
  try {
565
593
  response = await fetchImpl(
566
594
  `${identity.apiUrl}/payments/${encodeURIComponent(paymentId)}/sign-context`,
567
595
  {
568
- headers: { Authorization: `Bearer ${identity.apiKey}` },
596
+ headers: signContextHeaders(identity, clientIdentity),
569
597
  signal: AbortSignal.timeout(timeoutMs)
570
598
  }
571
599
  );
@@ -599,7 +627,7 @@ async function fetchDirectSignContext(identity, paymentId, fetchImpl = fetch, ti
599
627
  throw new HavenSignContextError(
600
628
  `Haven refused the direct-payment signing-context fetch for ${paymentId}: ${detail}` + (response.status === 404 ? " \u2014 either the payment_id is not from this agent\u2019s own POST /payments call, or this Haven backend predates #3271. Pass payload_hash and typed_data_b64 from the haven_send / haven_pay result, unchanged, instead." : body.error_code === "expired" ? " The payment window has expired; call haven_send / haven_pay again with the same idempotency_key." : ""),
601
629
  "SIGN_CONTEXT_REFUSED",
602
- { httpStatus: response.status, errorCode: typeof body.error_code === "string" ? body.error_code : void 0 },
630
+ refusalOf(response.status, body),
603
631
  paymentId,
604
632
  "direct"
605
633
  );
@@ -620,7 +648,8 @@ async function fetchDirectSignContext(identity, paymentId, fetchImpl = fetch, ti
620
648
  payloadHash: signData.hash,
621
649
  typedData: signData.typed_data,
622
650
  status: typeof body.status === "string" ? body.status : "pending_signature",
623
- expiresAt: typeof body.expires_at === "string" ? body.expires_at : void 0
651
+ expiresAt: typeof body.expires_at === "string" ? body.expires_at : void 0,
652
+ clientUpdate: edge.readClientUpdate(body.client_update)
624
653
  };
625
654
  }
626
655
 
@@ -1147,6 +1176,9 @@ function parseFetchedExpected(fetched) {
1147
1176
  }
1148
1177
  return parsed.data;
1149
1178
  }
1179
+ function clientUpdateField(resolved) {
1180
+ return resolved?.ctx.clientUpdate ? { client_update: resolved.ctx.clientUpdate } : {};
1181
+ }
1150
1182
  function resolveTypedData(args) {
1151
1183
  if (!args.typed_data_b64) return args.typed_data;
1152
1184
  try {
@@ -1180,14 +1212,26 @@ function createToolHandlers(signer, options = {}) {
1180
1212
  );
1181
1213
  }
1182
1214
  try {
1183
- const ctx = await fetchX402SignContext(identity, args.payment_id, options.signContext?.fetchImpl);
1215
+ const ctx = await fetchX402SignContext(
1216
+ identity,
1217
+ args.payment_id,
1218
+ options.signContext?.fetchImpl,
1219
+ void 0,
1220
+ options.signContext?.clientIdentity
1221
+ );
1184
1222
  checkPayloadHashMatch(args.payload_hash, ctx.payloadHash, args.payment_id);
1185
1223
  return { kind: "x402", ctx };
1186
1224
  } catch (err) {
1187
1225
  if (opts.allowDirectFallback && err instanceof HavenSignContextError && err.http_status === 409 && err.backend_error_code === "sign_context_unavailable") {
1188
1226
  let ctx;
1189
1227
  try {
1190
- ctx = await fetchDirectSignContext(identity, args.payment_id, options.signContext?.fetchImpl);
1228
+ ctx = await fetchDirectSignContext(
1229
+ identity,
1230
+ args.payment_id,
1231
+ options.signContext?.fetchImpl,
1232
+ void 0,
1233
+ options.signContext?.clientIdentity
1234
+ );
1191
1235
  } catch (directErr) {
1192
1236
  if (directErr instanceof HavenSignContextError && directErr.http_status === 409 && directErr.backend_error_code === "sign_context_unavailable") {
1193
1237
  throw err;
@@ -1237,12 +1281,12 @@ function createToolHandlers(signer, options = {}) {
1237
1281
  assertBoundDirectPaymentUserOp2(typedData, signer.delegateAddress);
1238
1282
  const signature = await signer.signDelegationTypedData(typedData);
1239
1283
  await auditSigning("haven_sign", viem.hashTypedData(typedData));
1240
- return { signature };
1284
+ return { signature, ...clientUpdateField(resolved) };
1241
1285
  }
1242
1286
  throw new HavenBareHashRefusedError();
1243
1287
  }
1244
1288
  await auditSigning("haven_sign", viem.hashTypedData(typedData));
1245
- return { signature: result.signature, x402_binding: result.x402Binding };
1289
+ return { signature: result.signature, x402_binding: result.x402Binding, ...clientUpdateField(resolved) };
1246
1290
  }),
1247
1291
  haven_x402_sign_header: async (input) => runTool(async () => {
1248
1292
  const args = parse("haven_x402_sign_header", coercePaymentRequired(input));
@@ -1288,7 +1332,8 @@ function createToolHandlers(signer, options = {}) {
1288
1332
  signature: funding.signature,
1289
1333
  x402_binding: funding.x402Binding,
1290
1334
  payment_header: header.paymentHeader,
1291
- accepted: header.accepted
1335
+ accepted: header.accepted,
1336
+ ...clientUpdateField(resolved)
1292
1337
  };
1293
1338
  }),
1294
1339
  haven_sign_sweep_delegate: async (input) => runTool(async () => {
@@ -1427,6 +1472,7 @@ function normalizeError(err) {
1427
1472
  ...err.retry_with_new_quote ? { retry_with_new_quote: true } : {},
1428
1473
  ...err.http_status !== void 0 ? { http_status: err.http_status } : {},
1429
1474
  ...err.backend_error_code !== void 0 ? { backend_error_code: err.backend_error_code } : {},
1475
+ ...err.client_update ? { client_update: err.client_update } : {},
1430
1476
  // #3103: the typed step the error decided beside its action.
1431
1477
  ...err.next_tool ? { next_tool: err.next_tool } : {},
1432
1478
  ...err.next_tool_server ? { next_tool_server: err.next_tool_server } : {},
@@ -1601,7 +1647,7 @@ async function writeAckFile(path$1, hash) {
1601
1647
  );
1602
1648
  }
1603
1649
  var SIGNER_NAME = "@haven_ai/signer";
1604
- var SIGNER_VERSION = "0.0.0-dev.202609250737.3bd5a51";
1650
+ var SIGNER_VERSION = "0.0.0-dev.202609251409.98ed67a";
1605
1651
  async function resolveEdgeSigner(options = {}) {
1606
1652
  const { signer } = await resolveSignerRuntime(options);
1607
1653
  return signer;
@@ -1650,7 +1696,10 @@ function buildSignerMcpServer(signer, options = {}) {
1650
1696
  // The signer CORE stays network-free; fetched bytes still pass the same
1651
1697
  // binding verification + digest re-derivation as tool-argument bytes.
1652
1698
  signContext: {
1653
- loadIdentity: () => loadHavenIdentity(credentialsPath)
1699
+ loadIdentity: () => loadHavenIdentity(credentialsPath),
1700
+ // #3303: the backend refuses a signer below a minimum it has set, at
1701
+ // sign-context — the one place this signer meets it.
1702
+ clientIdentity: edge.havenClientIdentity(SIGNER_NAME, SIGNER_VERSION)
1654
1703
  }
1655
1704
  });
1656
1705
  const registerTool = server.tool.bind(server);