@haven_ai/signer 0.3.0-alpha.0 → 0.4.0-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/README.md +8 -3
- package/dist/cli.cjs +108 -10
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +154 -56
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +109 -10
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +225 -3
- package/dist/index.d.ts +225 -3
- package/dist/index.js +155 -57
- package/dist/index.js.map +1 -1
- package/package.json +4 -3
package/dist/cli.js
CHANGED
|
@@ -5,11 +5,12 @@ import { randomUUID, createHash } from 'crypto';
|
|
|
5
5
|
import { readFile, mkdir, writeFile, stat, appendFile } from 'fs/promises';
|
|
6
6
|
import { homedir } from 'os';
|
|
7
7
|
import { resolve, dirname, join } from 'path';
|
|
8
|
-
import { isSupportedNodeVersion, unsupportedNodeVersionMessage, addressFromKey, HavenSigningError, buildSweepTypedData, selectStandardPaymentOption, HavenApiError, toStandardPaymentRequirements, decodeBase64Json, encodeBase64Json, x402V2PaymentEnvelope, connectorRerunCommand, buildSweepAuthorizationMessage, verifySignature, HavenError, AgentPaymentFailureCode, x402AuthorizationAmount, buildX402ExpectedMessage, signHash, SignerRefusalCode, SIGNER_UPDATE_FALLBACK, HavenUnsupportedSignerVersionError, AgentPaymentNextAction } from '@haven_ai/sdk';
|
|
8
|
+
import { createNextStepBuilder, isSupportedNodeVersion, unsupportedNodeVersionMessage, addressFromKey, HavenSigningError, buildSweepTypedData, selectStandardPaymentOption, HavenApiError, toStandardPaymentRequirements, decodeBase64Json, encodeBase64Json, x402V2PaymentEnvelope, connectorRerunCommand, buildSweepAuthorizationMessage, verifySignature, HavenError, AgentPaymentFailureCode, x402AuthorizationAmount, buildX402ExpectedMessage, signHash, SignerRefusalCode, SIGNER_UPDATE_FALLBACK, HavenUnsupportedSignerVersionError, AgentPaymentNextAction } from '@haven_ai/sdk';
|
|
9
9
|
import { recoverTypedDataAddress, hashTypedData, hashMessage } from 'viem';
|
|
10
10
|
import { privateKeyToAccount } from 'viem/accounts';
|
|
11
11
|
import { exact } from 'x402/schemes';
|
|
12
|
-
import { z } from 'zod/v3';
|
|
12
|
+
import { z as z$1 } from 'zod/v3';
|
|
13
|
+
import { z } from 'zod';
|
|
13
14
|
|
|
14
15
|
function defaultSigningAuditPath(credentialsPath) {
|
|
15
16
|
if (credentialsPath) return resolve(`${credentialsPath}.signer-audit.jsonl`);
|
|
@@ -529,9 +530,44 @@ function signerInstructions() {
|
|
|
529
530
|
"",
|
|
530
531
|
"A version-mismatch refusal from haven_sign / haven_sign_x402 / haven_sign_sweep_delegate is",
|
|
531
532
|
"machine-readable, not just prose: it carries code, supported_versions, received_version, and",
|
|
532
|
-
"fallback fields alongside the message, so you can branch on it directly."
|
|
533
|
+
"fallback fields alongside the message, so you can branch on it directly. Every signer",
|
|
534
|
+
"refusal also carries the next-step family: next_tool_name + next_tool_server_role when a",
|
|
535
|
+
"hosted tool follows (resolve the role against your own server names), else",
|
|
536
|
+
"next_tool_omitted_reason saying why not."
|
|
533
537
|
].join("\n");
|
|
534
538
|
}
|
|
539
|
+
var SIGNER_HOSTED_HANDOFF_SHAPES = {
|
|
540
|
+
haven_get_payment_status: { payment_id: z.string().min(1) }
|
|
541
|
+
};
|
|
542
|
+
function target(shape) {
|
|
543
|
+
const schema = z.object(shape).strict();
|
|
544
|
+
return {
|
|
545
|
+
role: "hosted",
|
|
546
|
+
validate: (input) => {
|
|
547
|
+
const r = schema.safeParse(input ?? {});
|
|
548
|
+
return r.success ? null : r.error.errors.map((e) => `${e.path.join(".") || "(root)"}: ${e.message}`).join("; ");
|
|
549
|
+
}
|
|
550
|
+
};
|
|
551
|
+
}
|
|
552
|
+
var SIGNER_NEXT_STEP_TARGETS = {
|
|
553
|
+
haven_get_payment_status: target(SIGNER_HOSTED_HANDOFF_SHAPES.haven_get_payment_status)
|
|
554
|
+
};
|
|
555
|
+
var nextStep = createNextStepBuilder(SIGNER_NEXT_STEP_TARGETS);
|
|
556
|
+
function signerRefusalStep(input) {
|
|
557
|
+
return nextStep({ ...input, safeToContinue: false, reason: "" });
|
|
558
|
+
}
|
|
559
|
+
function nextStepWireFields(step) {
|
|
560
|
+
return {
|
|
561
|
+
...step.next_tool ? { next_tool: step.next_tool } : {},
|
|
562
|
+
...step.next_tool_server ? { next_tool_server: step.next_tool_server } : {},
|
|
563
|
+
...step.next_tool_name ? { next_tool_name: step.next_tool_name } : {},
|
|
564
|
+
...step.next_tool_server_role ? { next_tool_server_role: step.next_tool_server_role } : {},
|
|
565
|
+
...step.next_arguments ? { next_arguments: step.next_arguments } : {},
|
|
566
|
+
...step.next_tool_omitted_reason ? { next_tool_omitted_reason: step.next_tool_omitted_reason } : {}
|
|
567
|
+
};
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
// src/sign-context.ts
|
|
535
571
|
var HavenSignContextError = class extends HavenSigningError {
|
|
536
572
|
/**
|
|
537
573
|
* #3001 / #3010 review: the recovery path, when signing OTHER bytes is
|
|
@@ -551,28 +587,63 @@ var HavenSignContextError = class extends HavenSigningError {
|
|
|
551
587
|
* plain `HavenError` branch already does for that code.
|
|
552
588
|
*/
|
|
553
589
|
next_action;
|
|
590
|
+
/**
|
|
591
|
+
* #3103 (epic #3105, decisions 1 and 3): the typed next step beside the
|
|
592
|
+
* action. A refusal Haven made (`SIGN_CONTEXT_REFUSED`, not expired) names
|
|
593
|
+
* the hosted status read with the payment id; a transport failure or a
|
|
594
|
+
* malformed body names no tool — the remedy is re-running the SAME quote
|
|
595
|
+
* tool with `include_signing_payload: true` — and an expired window names
|
|
596
|
+
* none either (which quote tool depends on the flow). Never null: a step
|
|
597
|
+
* with no tool carries `next_tool_omitted_reason`. Additive: the class is
|
|
598
|
+
* not exported from the package index — the published surface is the
|
|
599
|
+
* `ToolFailure` envelope `tools.ts` builds from these fields — and every
|
|
600
|
+
* constructor call site is in this file.
|
|
601
|
+
*/
|
|
602
|
+
next_tool;
|
|
603
|
+
next_tool_server;
|
|
604
|
+
next_tool_name;
|
|
605
|
+
next_tool_server_role;
|
|
606
|
+
next_arguments;
|
|
607
|
+
next_tool_omitted_reason;
|
|
554
608
|
retry_with_new_quote;
|
|
555
609
|
/** Present only for `SIGN_CONTEXT_REFUSED` (the backend's HTTP status). */
|
|
556
610
|
http_status;
|
|
557
611
|
/** Present only for `SIGN_CONTEXT_REFUSED`: the backend's own `error_code`. */
|
|
558
612
|
backend_error_code;
|
|
559
|
-
constructor(message, code, refusal) {
|
|
613
|
+
constructor(message, code, refusal, paymentId) {
|
|
560
614
|
super(message);
|
|
561
615
|
this.code = code;
|
|
562
616
|
this.name = "HavenSignContextError";
|
|
617
|
+
let step;
|
|
563
618
|
if (code === "SIGN_CONTEXT_REFUSED") {
|
|
564
619
|
this.http_status = refusal?.httpStatus;
|
|
565
620
|
this.backend_error_code = refusal?.errorCode;
|
|
566
621
|
if (refusal?.httpStatus === 410 || refusal?.errorCode === "expired") {
|
|
567
622
|
this.next_action = AgentPaymentNextAction.PaymentWindowExpired;
|
|
568
623
|
this.retry_with_new_quote = true;
|
|
624
|
+
step = signerRefusalStep({
|
|
625
|
+
nextAction: AgentPaymentNextAction.PaymentWindowExpired,
|
|
626
|
+
nextTool: null,
|
|
627
|
+
nextToolOmittedReason: "re-run the hosted quote tool you called with the same idempotency_key; which one depends on the flow"
|
|
628
|
+
});
|
|
569
629
|
} else {
|
|
570
630
|
this.next_action = AgentPaymentNextAction.StopAndTellUser;
|
|
631
|
+
step = signerRefusalStep({
|
|
632
|
+
nextAction: AgentPaymentNextAction.StopAndTellUser,
|
|
633
|
+
nextTool: "haven_get_payment_status",
|
|
634
|
+
nextArguments: { payment_id: paymentId }
|
|
635
|
+
});
|
|
571
636
|
}
|
|
572
637
|
} else {
|
|
573
638
|
this.fallback = "typed_data_b64";
|
|
574
639
|
this.next_action = AgentPaymentNextAction.StopAndTellUser;
|
|
640
|
+
step = signerRefusalStep({
|
|
641
|
+
nextAction: AgentPaymentNextAction.StopAndTellUser,
|
|
642
|
+
nextTool: null,
|
|
643
|
+
nextToolOmittedReason: "re-run the SAME hosted quote tool with the same idempotency_key and include_signing_payload: true, then pass its typed_data_b64 to this signer"
|
|
644
|
+
});
|
|
575
645
|
}
|
|
646
|
+
Object.assign(this, nextStepWireFields(step));
|
|
576
647
|
}
|
|
577
648
|
};
|
|
578
649
|
async function loadHavenIdentity(credentialsPath) {
|
|
@@ -604,7 +675,9 @@ async function fetchX402SignContext(identity, paymentId, fetchImpl = fetch, time
|
|
|
604
675
|
const timedOut = err instanceof Error && (err.name === "TimeoutError" || err.name === "AbortError");
|
|
605
676
|
throw new HavenSignContextError(
|
|
606
677
|
timedOut ? `Haven did not answer the signing-context fetch for ${paymentId} within ${timeoutMs} ms. Retry, or pass typed_data_b64 from the quote result instead.` : `Could not reach Haven to fetch the signing context for ${paymentId}: ${err instanceof Error ? err.message : String(err)}. Retry, or pass typed_data_b64 from the quote result instead.`,
|
|
607
|
-
timedOut ? "SIGN_CONTEXT_TIMEOUT" : "SIGN_CONTEXT_UNREACHABLE"
|
|
678
|
+
timedOut ? "SIGN_CONTEXT_TIMEOUT" : "SIGN_CONTEXT_UNREACHABLE",
|
|
679
|
+
void 0,
|
|
680
|
+
paymentId
|
|
608
681
|
);
|
|
609
682
|
}
|
|
610
683
|
let body;
|
|
@@ -614,7 +687,9 @@ async function fetchX402SignContext(identity, paymentId, fetchImpl = fetch, time
|
|
|
614
687
|
if (err instanceof Error && (err.name === "TimeoutError" || err.name === "AbortError")) {
|
|
615
688
|
throw new HavenSignContextError(
|
|
616
689
|
`Haven did not finish sending the signing context for ${paymentId} within ${timeoutMs} ms. Retry, or pass typed_data_b64 from the quote result instead.`,
|
|
617
|
-
"SIGN_CONTEXT_TIMEOUT"
|
|
690
|
+
"SIGN_CONTEXT_TIMEOUT",
|
|
691
|
+
void 0,
|
|
692
|
+
paymentId
|
|
618
693
|
);
|
|
619
694
|
}
|
|
620
695
|
body = {};
|
|
@@ -624,7 +699,8 @@ async function fetchX402SignContext(identity, paymentId, fetchImpl = fetch, time
|
|
|
624
699
|
throw new HavenSignContextError(
|
|
625
700
|
`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." : ""),
|
|
626
701
|
"SIGN_CONTEXT_REFUSED",
|
|
627
|
-
{ httpStatus: response.status, errorCode: typeof body.error_code === "string" ? body.error_code : void 0 }
|
|
702
|
+
{ httpStatus: response.status, errorCode: typeof body.error_code === "string" ? body.error_code : void 0 },
|
|
703
|
+
paymentId
|
|
628
704
|
);
|
|
629
705
|
}
|
|
630
706
|
const signData = body.sign_data;
|
|
@@ -632,7 +708,9 @@ async function fetchX402SignContext(identity, paymentId, fetchImpl = fetch, time
|
|
|
632
708
|
if (!signData || typeof signData.hash !== "string" || !signData.typed_data || typeof signData.typed_data !== "object" || !x402Expected) {
|
|
633
709
|
throw new HavenSignContextError(
|
|
634
710
|
"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.",
|
|
635
|
-
"SIGN_CONTEXT_MALFORMED"
|
|
711
|
+
"SIGN_CONTEXT_MALFORMED",
|
|
712
|
+
void 0,
|
|
713
|
+
paymentId
|
|
636
714
|
);
|
|
637
715
|
}
|
|
638
716
|
const paymentRequired = body.payment_required;
|
|
@@ -646,54 +724,54 @@ async function fetchX402SignContext(identity, paymentId, fetchImpl = fetch, time
|
|
|
646
724
|
}
|
|
647
725
|
|
|
648
726
|
// src/tools.ts
|
|
649
|
-
var sweepAuthorizationSchema = z.object({
|
|
650
|
-
from: z.string().regex(/^0x[0-9a-fA-F]{40}$/, "from must be a 0x address"),
|
|
651
|
-
to: z.string().regex(/^0x[0-9a-fA-F]{40}$/, "to must be a 0x address"),
|
|
652
|
-
value: z.string().regex(/^[0-9]+$/, "value must be a decimal atomic amount"),
|
|
653
|
-
validAfter: z.string().regex(/^[0-9]+$/, "validAfter must be a decimal unix time"),
|
|
654
|
-
validBefore: z.string().regex(/^[0-9]+$/, "validBefore must be a decimal unix time"),
|
|
655
|
-
nonce: z.string().regex(/^0x[0-9a-fA-F]{64}$/, "nonce must be a 0x-prefixed 32-byte hex string"),
|
|
656
|
-
token: z.string().regex(/^0x[0-9a-fA-F]{40}$/, "token must be a 0x address"),
|
|
657
|
-
chainId: z.number().int().positive()
|
|
727
|
+
var sweepAuthorizationSchema = z$1.object({
|
|
728
|
+
from: z$1.string().regex(/^0x[0-9a-fA-F]{40}$/, "from must be a 0x address"),
|
|
729
|
+
to: z$1.string().regex(/^0x[0-9a-fA-F]{40}$/, "to must be a 0x address"),
|
|
730
|
+
value: z$1.string().regex(/^[0-9]+$/, "value must be a decimal atomic amount"),
|
|
731
|
+
validAfter: z$1.string().regex(/^[0-9]+$/, "validAfter must be a decimal unix time"),
|
|
732
|
+
validBefore: z$1.string().regex(/^[0-9]+$/, "validBefore must be a decimal unix time"),
|
|
733
|
+
nonce: z$1.string().regex(/^0x[0-9a-fA-F]{64}$/, "nonce must be a 0x-prefixed 32-byte hex string"),
|
|
734
|
+
token: z$1.string().regex(/^0x[0-9a-fA-F]{40}$/, "token must be a 0x address"),
|
|
735
|
+
chainId: z$1.number().int().positive()
|
|
658
736
|
});
|
|
659
|
-
var bindingVersionSchema = z.number().int().positive();
|
|
660
|
-
var sweepExpectedAuthSchema = z.object({
|
|
737
|
+
var bindingVersionSchema = z$1.number().int().positive();
|
|
738
|
+
var sweepExpectedAuthSchema = z$1.object({
|
|
661
739
|
version: bindingVersionSchema,
|
|
662
|
-
message: z.string().min(1),
|
|
663
|
-
signature: z.string().regex(/^0x[0-9a-fA-F]+$/, "signature must be a 0x-prefixed hex string"),
|
|
664
|
-
signer: z.string().regex(/^0x[0-9a-fA-F]{40}$/, "signer must be a 0x address")
|
|
740
|
+
message: z$1.string().min(1),
|
|
741
|
+
signature: z$1.string().regex(/^0x[0-9a-fA-F]+$/, "signature must be a 0x-prefixed hex string"),
|
|
742
|
+
signer: z$1.string().regex(/^0x[0-9a-fA-F]{40}$/, "signer must be a 0x address")
|
|
665
743
|
});
|
|
666
744
|
var x402ExpectedShape = {
|
|
667
|
-
payment_id: z.string().min(1),
|
|
668
|
-
payload_hash: z.string().regex(/^0x[0-9a-fA-F]+$/, "payload_hash must be a 0x-prefixed hex string"),
|
|
669
|
-
resource_url: z.string().url(),
|
|
670
|
-
merchant_to: z.string().min(1),
|
|
671
|
-
amount: z.string().min(1),
|
|
672
|
-
asset: z.string().min(1),
|
|
673
|
-
network: z.string().min(1),
|
|
745
|
+
payment_id: z$1.string().min(1),
|
|
746
|
+
payload_hash: z$1.string().regex(/^0x[0-9a-fA-F]+$/, "payload_hash must be a 0x-prefixed hex string"),
|
|
747
|
+
resource_url: z$1.string().url(),
|
|
748
|
+
merchant_to: z$1.string().min(1),
|
|
749
|
+
amount: z$1.string().min(1),
|
|
750
|
+
asset: z$1.string().min(1),
|
|
751
|
+
network: z$1.string().min(1),
|
|
674
752
|
// Required: expires_at is folded into the Haven-signed binding message, so the
|
|
675
753
|
// signer must receive the exact same value to reconstruct a matching message.
|
|
676
754
|
// Omitting it used to fail downstream with a cryptic "authentication message is
|
|
677
755
|
// invalid" — making it required surfaces a clear INVALID_INPUT at the boundary.
|
|
678
|
-
expires_at: z.string().min(1),
|
|
756
|
+
expires_at: z$1.string().min(1),
|
|
679
757
|
// #1138: present on a delegation-rail (v2) context. It commits to the EIP-712
|
|
680
758
|
// typed data the account validates; the signer refuses to raw-sign the bare
|
|
681
759
|
// hash when it is present, and refuses to sign typed data when it is absent.
|
|
682
|
-
typed_data_hash: z.string().regex(/^0x[0-9a-fA-F]+$/, "typed_data_hash must be a 0x-prefixed hex string").optional(),
|
|
760
|
+
typed_data_hash: z$1.string().regex(/^0x[0-9a-fA-F]+$/, "typed_data_hash must be a 0x-prefixed hex string").optional(),
|
|
683
761
|
// #1690: present on a v3 context. The delegate this quote was created FOR —
|
|
684
762
|
// the signer refuses to sign when it is not its own. Inside the Haven-signed
|
|
685
763
|
// message, so stripping it here would only break the binding signature.
|
|
686
|
-
payer_delegate: z.string().regex(/^0x[0-9a-fA-F]{40}$/, "payer_delegate must be a 0x-prefixed address").optional(),
|
|
687
|
-
payer_agent_id: z.string().min(1).optional(),
|
|
688
|
-
auth: z.object({
|
|
764
|
+
payer_delegate: z$1.string().regex(/^0x[0-9a-fA-F]{40}$/, "payer_delegate must be a 0x-prefixed address").optional(),
|
|
765
|
+
payer_agent_id: z$1.string().min(1).optional(),
|
|
766
|
+
auth: z$1.object({
|
|
689
767
|
// Open at the boundary, enforced in the signer — see bindingVersionSchema.
|
|
690
768
|
version: bindingVersionSchema,
|
|
691
|
-
message: z.string().min(1),
|
|
692
|
-
signature: z.string().regex(/^0x[0-9a-fA-F]+$/, "signature must be a 0x-prefixed hex string"),
|
|
693
|
-
signer: z.string().min(1)
|
|
769
|
+
message: z$1.string().min(1),
|
|
770
|
+
signature: z$1.string().regex(/^0x[0-9a-fA-F]+$/, "signature must be a 0x-prefixed hex string"),
|
|
771
|
+
signer: z$1.string().min(1)
|
|
694
772
|
})
|
|
695
773
|
};
|
|
696
|
-
var x402ExpectedSchema = z.object(x402ExpectedShape);
|
|
774
|
+
var x402ExpectedSchema = z$1.object(x402ExpectedShape);
|
|
697
775
|
var toolSchemas = {
|
|
698
776
|
haven_sign_sweep_delegate: {
|
|
699
777
|
// The authorization fields prepared by Haven's POST /sweep/prepare. Passed
|
|
@@ -708,17 +786,17 @@ var toolSchemas = {
|
|
|
708
786
|
// payload + expected context from Haven itself (authenticated with the
|
|
709
787
|
// locally-stored agent credential), so no bulky bytes ever cross the
|
|
710
788
|
// model. Pass payment_id ALONE for a delegation-rail x402 funding intent.
|
|
711
|
-
payment_id: z.string().min(1).optional(),
|
|
789
|
+
payment_id: z$1.string().min(1).optional(),
|
|
712
790
|
// The unsigned hash from haven_pay / haven_pay_x402_quote (payload_hash).
|
|
713
791
|
// Optional when payment_id is supplied (the fetch carries it).
|
|
714
|
-
payload_hash: z.string().regex(/^0x[0-9a-fA-F]+$/, "payload_hash must be a 0x-prefixed hex string").optional(),
|
|
792
|
+
payload_hash: z$1.string().regex(/^0x[0-9a-fA-F]+$/, "payload_hash must be a 0x-prefixed hex string").optional(),
|
|
715
793
|
// Pass x402.expected from hosted haven_pay_x402_quote when this hash funds
|
|
716
794
|
// a standard x402 merchant retry. The signer records it locally and returns
|
|
717
795
|
// an opaque x402_binding for the later header-signing step.
|
|
718
796
|
x402_expected: x402ExpectedSchema.optional(),
|
|
719
797
|
// #1138: delegation-rail intents sign THIS, not payload_hash. Object-typed
|
|
720
798
|
// (not z.unknown()) so MCP clients embed it as JSON rather than a string.
|
|
721
|
-
typed_data: z.record(z.string(), z.unknown()).optional(),
|
|
799
|
+
typed_data: z$1.record(z$1.string(), z$1.unknown()).optional(),
|
|
722
800
|
// #1255: the same payload as ONE opaque base64 string, exactly as returned
|
|
723
801
|
// by the hosted tools. Preferred over typed_data when both are present —
|
|
724
802
|
// an agent re-emitting multi-KB nested JSON between tool calls is the
|
|
@@ -727,35 +805,36 @@ var toolSchemas = {
|
|
|
727
805
|
// Bounded: a realistic redemption payload is ~10KB encoded; 256KB is
|
|
728
806
|
// generous headroom while keeping the offline signer from materializing
|
|
729
807
|
// arbitrarily large caller input.
|
|
730
|
-
typed_data_b64: z.string().min(1).max(262144).optional()
|
|
808
|
+
typed_data_b64: z$1.string().min(1).max(262144).optional()
|
|
731
809
|
},
|
|
732
810
|
haven_x402_sign_header: {
|
|
733
811
|
// The parsed HTTP 402 PaymentRequired from the merchant. Typed as an object
|
|
734
812
|
// (not z.unknown(), which becomes empty JSON Schema `{}`) so MCP clients
|
|
735
813
|
// embed it as JSON rather than serialising the object to a string.
|
|
736
|
-
payment_required: z.record(z.string(), z.unknown()),
|
|
814
|
+
payment_required: z$1.record(z$1.string(), z$1.unknown()),
|
|
737
815
|
// Opaque binding returned by haven_sign when x402_expected was supplied.
|
|
738
|
-
x402_binding: z.string().min(1)
|
|
816
|
+
x402_binding: z$1.string().min(1)
|
|
739
817
|
},
|
|
740
818
|
haven_sign_x402: {
|
|
741
819
|
// #1263: preferred — pass payment_id (plus payment_required) and the
|
|
742
820
|
// signer fetches the exact signing payload + expected context itself.
|
|
743
|
-
payment_id: z.string().min(1).optional(),
|
|
821
|
+
payment_id: z$1.string().min(1).optional(),
|
|
744
822
|
// One-shot x402 signing: funding hash + merchant header in one local call.
|
|
745
823
|
// Both optional when payment_id is supplied (the fetch carries them).
|
|
746
|
-
payload_hash: z.string().regex(/^0x[0-9a-fA-F]+$/, "payload_hash must be a 0x-prefixed hex string").optional(),
|
|
824
|
+
payload_hash: z$1.string().regex(/^0x[0-9a-fA-F]+$/, "payload_hash must be a 0x-prefixed hex string").optional(),
|
|
747
825
|
x402_expected: x402ExpectedSchema.optional(),
|
|
748
826
|
// #1355: optional when payment_id is supplied — the signer's context fetch
|
|
749
827
|
// carries the stored 402 PaymentRequired, so `{ payment_id }` alone is the
|
|
750
828
|
// preferred call. Still required (via the runtime check in the handler)
|
|
751
829
|
// for the quote-based fallback path without a payment_id.
|
|
752
|
-
payment_required: z.record(z.string(), z.unknown()).optional(),
|
|
830
|
+
payment_required: z$1.record(z$1.string(), z$1.unknown()).optional(),
|
|
753
831
|
// #1138: delegation-rail intents sign THIS, not payload_hash. Object-typed
|
|
754
832
|
// (not z.unknown()) so MCP clients embed it as JSON rather than a string.
|
|
755
|
-
typed_data: z.record(z.string(), z.unknown()).optional(),
|
|
833
|
+
typed_data: z$1.record(z$1.string(), z$1.unknown()).optional(),
|
|
756
834
|
// #1255: see haven_sign.typed_data_b64 — the copy-through-safe form.
|
|
757
|
-
typed_data_b64: z.string().min(1).max(262144).optional()
|
|
835
|
+
typed_data_b64: z$1.string().min(1).max(262144).optional()
|
|
758
836
|
}
|
|
837
|
+
// #3101: keys survive on the type (see the hosted server's contracts.ts).
|
|
759
838
|
};
|
|
760
839
|
var SIGN_DESCRIPTION = [
|
|
761
840
|
"Sign an unsigned Haven payment hash with the local delegate key. The delegate key never leaves",
|
|
@@ -853,7 +932,7 @@ function toExpectedX402(raw) {
|
|
|
853
932
|
};
|
|
854
933
|
}
|
|
855
934
|
function parseFetchedExpected(fetched) {
|
|
856
|
-
const parsed = z.object(x402ExpectedShape).safeParse(fetched.x402Expected);
|
|
935
|
+
const parsed = z$1.object(x402ExpectedShape).safeParse(fetched.x402Expected);
|
|
857
936
|
if (!parsed.success) {
|
|
858
937
|
throw new HavenSigningError(
|
|
859
938
|
`The Haven sign-context response carried a malformed x402_expected \u2014 the backend may predate #1263. Pass payload_hash + x402_expected from the quote result instead. Underlying: ${parsed.error.issues[0]?.message ?? "schema mismatch"}`
|
|
@@ -1005,7 +1084,7 @@ function createToolHandlers(signer, options = {}) {
|
|
|
1005
1084
|
}
|
|
1006
1085
|
}
|
|
1007
1086
|
function parse(name, input) {
|
|
1008
|
-
return z.object(toolSchemas[name]).parse(input ?? {});
|
|
1087
|
+
return z$1.object(toolSchemas[name]).parse(input ?? {});
|
|
1009
1088
|
}
|
|
1010
1089
|
function coerceX402Expected(input) {
|
|
1011
1090
|
if (!input || typeof input !== "object") return input;
|
|
@@ -1044,7 +1123,7 @@ async function runTool(fn) {
|
|
|
1044
1123
|
}
|
|
1045
1124
|
}
|
|
1046
1125
|
function normalizeError(err) {
|
|
1047
|
-
if (err instanceof z.ZodError) {
|
|
1126
|
+
if (err instanceof z$1.ZodError) {
|
|
1048
1127
|
return {
|
|
1049
1128
|
success: false,
|
|
1050
1129
|
code: "INVALID_INPUT",
|
|
@@ -1060,7 +1139,13 @@ function normalizeError(err) {
|
|
|
1060
1139
|
supported_versions: [...err.supportedVersions],
|
|
1061
1140
|
received_version: err.receivedVersion,
|
|
1062
1141
|
fallback: err.fallback,
|
|
1063
|
-
next_action: AgentPaymentNextAction.StopAndTellUser
|
|
1142
|
+
next_action: AgentPaymentNextAction.StopAndTellUser,
|
|
1143
|
+
// #3103: no tool can fix a version skew from inside the call.
|
|
1144
|
+
...nextStepWireFields(signerRefusalStep({
|
|
1145
|
+
nextAction: AgentPaymentNextAction.StopAndTellUser,
|
|
1146
|
+
nextTool: null,
|
|
1147
|
+
nextToolOmittedReason: "update @haven_ai/signer by re-running the connector, then repeat the same call"
|
|
1148
|
+
}))
|
|
1064
1149
|
};
|
|
1065
1150
|
}
|
|
1066
1151
|
if (err instanceof HavenSignContextError) {
|
|
@@ -1072,7 +1157,14 @@ function normalizeError(err) {
|
|
|
1072
1157
|
...err.fallback !== void 0 ? { fallback: err.fallback } : {},
|
|
1073
1158
|
...err.retry_with_new_quote ? { retry_with_new_quote: true } : {},
|
|
1074
1159
|
...err.http_status !== void 0 ? { http_status: err.http_status } : {},
|
|
1075
|
-
...err.backend_error_code !== void 0 ? { backend_error_code: err.backend_error_code } : {}
|
|
1160
|
+
...err.backend_error_code !== void 0 ? { backend_error_code: err.backend_error_code } : {},
|
|
1161
|
+
// #3103: the typed step the error decided beside its action.
|
|
1162
|
+
...err.next_tool ? { next_tool: err.next_tool } : {},
|
|
1163
|
+
...err.next_tool_server ? { next_tool_server: err.next_tool_server } : {},
|
|
1164
|
+
...err.next_tool_name ? { next_tool_name: err.next_tool_name } : {},
|
|
1165
|
+
...err.next_tool_server_role ? { next_tool_server_role: err.next_tool_server_role } : {},
|
|
1166
|
+
...err.next_arguments ? { next_arguments: err.next_arguments } : {},
|
|
1167
|
+
...err.next_tool_omitted_reason ? { next_tool_omitted_reason: err.next_tool_omitted_reason } : {}
|
|
1076
1168
|
};
|
|
1077
1169
|
}
|
|
1078
1170
|
if (err instanceof HavenSigningError) {
|
|
@@ -1091,7 +1183,13 @@ function normalizeError(err) {
|
|
|
1091
1183
|
...err.code === AgentPaymentFailureCode.PaymentWindowExpired ? {
|
|
1092
1184
|
next_action: AgentPaymentNextAction.PaymentWindowExpired,
|
|
1093
1185
|
retry_with_new_quote: true,
|
|
1094
|
-
suggested_tool: "haven_pay_mcp_tool"
|
|
1186
|
+
suggested_tool: "haven_pay_mcp_tool",
|
|
1187
|
+
// #3103: same omission as the hosted window-expired helper.
|
|
1188
|
+
...nextStepWireFields(signerRefusalStep({
|
|
1189
|
+
nextAction: AgentPaymentNextAction.PaymentWindowExpired,
|
|
1190
|
+
nextTool: null,
|
|
1191
|
+
nextToolOmittedReason: "re-run the hosted quote tool you called with the same idempotency_key; which one depends on the flow (suggested_tool names the MCP one)"
|
|
1192
|
+
}))
|
|
1095
1193
|
} : {}
|
|
1096
1194
|
};
|
|
1097
1195
|
}
|
|
@@ -1333,7 +1431,7 @@ async function warnIfCredentialFilePermissive(path, log = (message) => process.s
|
|
|
1333
1431
|
|
|
1334
1432
|
// src/server.ts
|
|
1335
1433
|
var SIGNER_NAME = "@haven_ai/signer";
|
|
1336
|
-
var SIGNER_VERSION = "0.
|
|
1434
|
+
var SIGNER_VERSION = "0.4.0-alpha.0";
|
|
1337
1435
|
async function resolveSignerRuntime(options = {}) {
|
|
1338
1436
|
assertSupportedNodeVersion(options.nodeVersion);
|
|
1339
1437
|
if (options.delegateKey) {
|