@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/README.md +1 -0
- package/dist/cli.cjs +65 -16
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +66 -17
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +65 -16
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +7 -3
- package/dist/index.d.ts +7 -3
- package/dist/index.js +66 -17
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
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 (
|
|
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
|
-
|
|
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:
|
|
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
|
-
|
|
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:
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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.
|
|
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);
|