@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/index.js
CHANGED
|
@@ -2,13 +2,14 @@ import { randomUUID, createHash } from 'crypto';
|
|
|
2
2
|
import { recoverTypedDataAddress, hashTypedData, hashMessage } from 'viem';
|
|
3
3
|
import { privateKeyToAccount } from 'viem/accounts';
|
|
4
4
|
import { exact } from 'x402/schemes';
|
|
5
|
-
import { addressFromKey, HavenSigningError, buildSweepTypedData, selectStandardPaymentOption, HavenApiError, toStandardPaymentRequirements, decodeBase64Json, encodeBase64Json, x402V2PaymentEnvelope, buildSweepAuthorizationMessage, verifySignature, x402AuthorizationAmount, SignerRefusalCode, connectorRerunCommand, SIGNER_UPDATE_FALLBACK, HavenUnsupportedSignerVersionError, buildX402ExpectedMessage, HavenError, AgentPaymentFailureCode, AgentPaymentNextAction, isSupportedNodeVersion, unsupportedNodeVersionMessage, signHash } from '@haven_ai/sdk';
|
|
5
|
+
import { createNextStepBuilder, addressFromKey, HavenSigningError, buildSweepTypedData, selectStandardPaymentOption, HavenApiError, toStandardPaymentRequirements, decodeBase64Json, encodeBase64Json, x402V2PaymentEnvelope, buildSweepAuthorizationMessage, verifySignature, x402AuthorizationAmount, SignerRefusalCode, connectorRerunCommand, SIGNER_UPDATE_FALLBACK, HavenUnsupportedSignerVersionError, buildX402ExpectedMessage, HavenError, AgentPaymentFailureCode, AgentPaymentNextAction, isSupportedNodeVersion, unsupportedNodeVersionMessage, signHash } from '@haven_ai/sdk';
|
|
6
6
|
import { readFile, stat, mkdir, appendFile, writeFile } from 'fs/promises';
|
|
7
7
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
8
8
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
9
9
|
import { homedir } from 'os';
|
|
10
10
|
import { resolve, dirname, join } from 'path';
|
|
11
|
-
import { z } from 'zod/v3';
|
|
11
|
+
import { z as z$1 } from 'zod/v3';
|
|
12
|
+
import { z } from 'zod';
|
|
12
13
|
|
|
13
14
|
// src/core.ts
|
|
14
15
|
var DELEGATION_MANAGER = "0xdb9B1e94B5b69Df7e401DDbedE43491141047dB3";
|
|
@@ -494,7 +495,10 @@ function signerInstructions() {
|
|
|
494
495
|
"",
|
|
495
496
|
"A version-mismatch refusal from haven_sign / haven_sign_x402 / haven_sign_sweep_delegate is",
|
|
496
497
|
"machine-readable, not just prose: it carries code, supported_versions, received_version, and",
|
|
497
|
-
"fallback fields alongside the message, so you can branch on it directly."
|
|
498
|
+
"fallback fields alongside the message, so you can branch on it directly. Every signer",
|
|
499
|
+
"refusal also carries the next-step family: next_tool_name + next_tool_server_role when a",
|
|
500
|
+
"hosted tool follows (resolve the role against your own server names), else",
|
|
501
|
+
"next_tool_omitted_reason saying why not."
|
|
498
502
|
].join("\n");
|
|
499
503
|
}
|
|
500
504
|
async function loadSignerCredentials(path = process.env.HAVEN_CREDENTIALS) {
|
|
@@ -640,6 +644,38 @@ function stableStringify(value) {
|
|
|
640
644
|
const object = value;
|
|
641
645
|
return `{${Object.keys(object).sort().map((key) => `${JSON.stringify(key)}:${stableStringify(object[key])}`).join(",")}}`;
|
|
642
646
|
}
|
|
647
|
+
var SIGNER_HOSTED_HANDOFF_SHAPES = {
|
|
648
|
+
haven_get_payment_status: { payment_id: z.string().min(1) }
|
|
649
|
+
};
|
|
650
|
+
function target(shape) {
|
|
651
|
+
const schema = z.object(shape).strict();
|
|
652
|
+
return {
|
|
653
|
+
role: "hosted",
|
|
654
|
+
validate: (input) => {
|
|
655
|
+
const r = schema.safeParse(input ?? {});
|
|
656
|
+
return r.success ? null : r.error.errors.map((e) => `${e.path.join(".") || "(root)"}: ${e.message}`).join("; ");
|
|
657
|
+
}
|
|
658
|
+
};
|
|
659
|
+
}
|
|
660
|
+
var SIGNER_NEXT_STEP_TARGETS = {
|
|
661
|
+
haven_get_payment_status: target(SIGNER_HOSTED_HANDOFF_SHAPES.haven_get_payment_status)
|
|
662
|
+
};
|
|
663
|
+
var nextStep = createNextStepBuilder(SIGNER_NEXT_STEP_TARGETS);
|
|
664
|
+
function signerRefusalStep(input) {
|
|
665
|
+
return nextStep({ ...input, safeToContinue: false, reason: "" });
|
|
666
|
+
}
|
|
667
|
+
function nextStepWireFields(step) {
|
|
668
|
+
return {
|
|
669
|
+
...step.next_tool ? { next_tool: step.next_tool } : {},
|
|
670
|
+
...step.next_tool_server ? { next_tool_server: step.next_tool_server } : {},
|
|
671
|
+
...step.next_tool_name ? { next_tool_name: step.next_tool_name } : {},
|
|
672
|
+
...step.next_tool_server_role ? { next_tool_server_role: step.next_tool_server_role } : {},
|
|
673
|
+
...step.next_arguments ? { next_arguments: step.next_arguments } : {},
|
|
674
|
+
...step.next_tool_omitted_reason ? { next_tool_omitted_reason: step.next_tool_omitted_reason } : {}
|
|
675
|
+
};
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
// src/sign-context.ts
|
|
643
679
|
var HavenSignContextError = class extends HavenSigningError {
|
|
644
680
|
/**
|
|
645
681
|
* #3001 / #3010 review: the recovery path, when signing OTHER bytes is
|
|
@@ -659,28 +695,63 @@ var HavenSignContextError = class extends HavenSigningError {
|
|
|
659
695
|
* plain `HavenError` branch already does for that code.
|
|
660
696
|
*/
|
|
661
697
|
next_action;
|
|
698
|
+
/**
|
|
699
|
+
* #3103 (epic #3105, decisions 1 and 3): the typed next step beside the
|
|
700
|
+
* action. A refusal Haven made (`SIGN_CONTEXT_REFUSED`, not expired) names
|
|
701
|
+
* the hosted status read with the payment id; a transport failure or a
|
|
702
|
+
* malformed body names no tool — the remedy is re-running the SAME quote
|
|
703
|
+
* tool with `include_signing_payload: true` — and an expired window names
|
|
704
|
+
* none either (which quote tool depends on the flow). Never null: a step
|
|
705
|
+
* with no tool carries `next_tool_omitted_reason`. Additive: the class is
|
|
706
|
+
* not exported from the package index — the published surface is the
|
|
707
|
+
* `ToolFailure` envelope `tools.ts` builds from these fields — and every
|
|
708
|
+
* constructor call site is in this file.
|
|
709
|
+
*/
|
|
710
|
+
next_tool;
|
|
711
|
+
next_tool_server;
|
|
712
|
+
next_tool_name;
|
|
713
|
+
next_tool_server_role;
|
|
714
|
+
next_arguments;
|
|
715
|
+
next_tool_omitted_reason;
|
|
662
716
|
retry_with_new_quote;
|
|
663
717
|
/** Present only for `SIGN_CONTEXT_REFUSED` (the backend's HTTP status). */
|
|
664
718
|
http_status;
|
|
665
719
|
/** Present only for `SIGN_CONTEXT_REFUSED`: the backend's own `error_code`. */
|
|
666
720
|
backend_error_code;
|
|
667
|
-
constructor(message, code, refusal) {
|
|
721
|
+
constructor(message, code, refusal, paymentId) {
|
|
668
722
|
super(message);
|
|
669
723
|
this.code = code;
|
|
670
724
|
this.name = "HavenSignContextError";
|
|
725
|
+
let step;
|
|
671
726
|
if (code === "SIGN_CONTEXT_REFUSED") {
|
|
672
727
|
this.http_status = refusal?.httpStatus;
|
|
673
728
|
this.backend_error_code = refusal?.errorCode;
|
|
674
729
|
if (refusal?.httpStatus === 410 || refusal?.errorCode === "expired") {
|
|
675
730
|
this.next_action = AgentPaymentNextAction.PaymentWindowExpired;
|
|
676
731
|
this.retry_with_new_quote = true;
|
|
732
|
+
step = signerRefusalStep({
|
|
733
|
+
nextAction: AgentPaymentNextAction.PaymentWindowExpired,
|
|
734
|
+
nextTool: null,
|
|
735
|
+
nextToolOmittedReason: "re-run the hosted quote tool you called with the same idempotency_key; which one depends on the flow"
|
|
736
|
+
});
|
|
677
737
|
} else {
|
|
678
738
|
this.next_action = AgentPaymentNextAction.StopAndTellUser;
|
|
739
|
+
step = signerRefusalStep({
|
|
740
|
+
nextAction: AgentPaymentNextAction.StopAndTellUser,
|
|
741
|
+
nextTool: "haven_get_payment_status",
|
|
742
|
+
nextArguments: { payment_id: paymentId }
|
|
743
|
+
});
|
|
679
744
|
}
|
|
680
745
|
} else {
|
|
681
746
|
this.fallback = "typed_data_b64";
|
|
682
747
|
this.next_action = AgentPaymentNextAction.StopAndTellUser;
|
|
748
|
+
step = signerRefusalStep({
|
|
749
|
+
nextAction: AgentPaymentNextAction.StopAndTellUser,
|
|
750
|
+
nextTool: null,
|
|
751
|
+
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"
|
|
752
|
+
});
|
|
683
753
|
}
|
|
754
|
+
Object.assign(this, nextStepWireFields(step));
|
|
684
755
|
}
|
|
685
756
|
};
|
|
686
757
|
async function loadHavenIdentity(credentialsPath) {
|
|
@@ -712,7 +783,9 @@ async function fetchX402SignContext(identity, paymentId, fetchImpl = fetch, time
|
|
|
712
783
|
const timedOut = err instanceof Error && (err.name === "TimeoutError" || err.name === "AbortError");
|
|
713
784
|
throw new HavenSignContextError(
|
|
714
785
|
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.`,
|
|
715
|
-
timedOut ? "SIGN_CONTEXT_TIMEOUT" : "SIGN_CONTEXT_UNREACHABLE"
|
|
786
|
+
timedOut ? "SIGN_CONTEXT_TIMEOUT" : "SIGN_CONTEXT_UNREACHABLE",
|
|
787
|
+
void 0,
|
|
788
|
+
paymentId
|
|
716
789
|
);
|
|
717
790
|
}
|
|
718
791
|
let body;
|
|
@@ -722,7 +795,9 @@ async function fetchX402SignContext(identity, paymentId, fetchImpl = fetch, time
|
|
|
722
795
|
if (err instanceof Error && (err.name === "TimeoutError" || err.name === "AbortError")) {
|
|
723
796
|
throw new HavenSignContextError(
|
|
724
797
|
`Haven did not finish sending the signing context for ${paymentId} within ${timeoutMs} ms. Retry, or pass typed_data_b64 from the quote result instead.`,
|
|
725
|
-
"SIGN_CONTEXT_TIMEOUT"
|
|
798
|
+
"SIGN_CONTEXT_TIMEOUT",
|
|
799
|
+
void 0,
|
|
800
|
+
paymentId
|
|
726
801
|
);
|
|
727
802
|
}
|
|
728
803
|
body = {};
|
|
@@ -732,7 +807,8 @@ async function fetchX402SignContext(identity, paymentId, fetchImpl = fetch, time
|
|
|
732
807
|
throw new HavenSignContextError(
|
|
733
808
|
`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." : ""),
|
|
734
809
|
"SIGN_CONTEXT_REFUSED",
|
|
735
|
-
{ httpStatus: response.status, errorCode: typeof body.error_code === "string" ? body.error_code : void 0 }
|
|
810
|
+
{ httpStatus: response.status, errorCode: typeof body.error_code === "string" ? body.error_code : void 0 },
|
|
811
|
+
paymentId
|
|
736
812
|
);
|
|
737
813
|
}
|
|
738
814
|
const signData = body.sign_data;
|
|
@@ -740,7 +816,9 @@ async function fetchX402SignContext(identity, paymentId, fetchImpl = fetch, time
|
|
|
740
816
|
if (!signData || typeof signData.hash !== "string" || !signData.typed_data || typeof signData.typed_data !== "object" || !x402Expected) {
|
|
741
817
|
throw new HavenSignContextError(
|
|
742
818
|
"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.",
|
|
743
|
-
"SIGN_CONTEXT_MALFORMED"
|
|
819
|
+
"SIGN_CONTEXT_MALFORMED",
|
|
820
|
+
void 0,
|
|
821
|
+
paymentId
|
|
744
822
|
);
|
|
745
823
|
}
|
|
746
824
|
const paymentRequired = body.payment_required;
|
|
@@ -754,54 +832,54 @@ async function fetchX402SignContext(identity, paymentId, fetchImpl = fetch, time
|
|
|
754
832
|
}
|
|
755
833
|
|
|
756
834
|
// src/tools.ts
|
|
757
|
-
var sweepAuthorizationSchema = z.object({
|
|
758
|
-
from: z.string().regex(/^0x[0-9a-fA-F]{40}$/, "from must be a 0x address"),
|
|
759
|
-
to: z.string().regex(/^0x[0-9a-fA-F]{40}$/, "to must be a 0x address"),
|
|
760
|
-
value: z.string().regex(/^[0-9]+$/, "value must be a decimal atomic amount"),
|
|
761
|
-
validAfter: z.string().regex(/^[0-9]+$/, "validAfter must be a decimal unix time"),
|
|
762
|
-
validBefore: z.string().regex(/^[0-9]+$/, "validBefore must be a decimal unix time"),
|
|
763
|
-
nonce: z.string().regex(/^0x[0-9a-fA-F]{64}$/, "nonce must be a 0x-prefixed 32-byte hex string"),
|
|
764
|
-
token: z.string().regex(/^0x[0-9a-fA-F]{40}$/, "token must be a 0x address"),
|
|
765
|
-
chainId: z.number().int().positive()
|
|
835
|
+
var sweepAuthorizationSchema = z$1.object({
|
|
836
|
+
from: z$1.string().regex(/^0x[0-9a-fA-F]{40}$/, "from must be a 0x address"),
|
|
837
|
+
to: z$1.string().regex(/^0x[0-9a-fA-F]{40}$/, "to must be a 0x address"),
|
|
838
|
+
value: z$1.string().regex(/^[0-9]+$/, "value must be a decimal atomic amount"),
|
|
839
|
+
validAfter: z$1.string().regex(/^[0-9]+$/, "validAfter must be a decimal unix time"),
|
|
840
|
+
validBefore: z$1.string().regex(/^[0-9]+$/, "validBefore must be a decimal unix time"),
|
|
841
|
+
nonce: z$1.string().regex(/^0x[0-9a-fA-F]{64}$/, "nonce must be a 0x-prefixed 32-byte hex string"),
|
|
842
|
+
token: z$1.string().regex(/^0x[0-9a-fA-F]{40}$/, "token must be a 0x address"),
|
|
843
|
+
chainId: z$1.number().int().positive()
|
|
766
844
|
});
|
|
767
|
-
var bindingVersionSchema = z.number().int().positive();
|
|
768
|
-
var sweepExpectedAuthSchema = z.object({
|
|
845
|
+
var bindingVersionSchema = z$1.number().int().positive();
|
|
846
|
+
var sweepExpectedAuthSchema = z$1.object({
|
|
769
847
|
version: bindingVersionSchema,
|
|
770
|
-
message: z.string().min(1),
|
|
771
|
-
signature: z.string().regex(/^0x[0-9a-fA-F]+$/, "signature must be a 0x-prefixed hex string"),
|
|
772
|
-
signer: z.string().regex(/^0x[0-9a-fA-F]{40}$/, "signer must be a 0x address")
|
|
848
|
+
message: z$1.string().min(1),
|
|
849
|
+
signature: z$1.string().regex(/^0x[0-9a-fA-F]+$/, "signature must be a 0x-prefixed hex string"),
|
|
850
|
+
signer: z$1.string().regex(/^0x[0-9a-fA-F]{40}$/, "signer must be a 0x address")
|
|
773
851
|
});
|
|
774
852
|
var x402ExpectedShape = {
|
|
775
|
-
payment_id: z.string().min(1),
|
|
776
|
-
payload_hash: z.string().regex(/^0x[0-9a-fA-F]+$/, "payload_hash must be a 0x-prefixed hex string"),
|
|
777
|
-
resource_url: z.string().url(),
|
|
778
|
-
merchant_to: z.string().min(1),
|
|
779
|
-
amount: z.string().min(1),
|
|
780
|
-
asset: z.string().min(1),
|
|
781
|
-
network: z.string().min(1),
|
|
853
|
+
payment_id: z$1.string().min(1),
|
|
854
|
+
payload_hash: z$1.string().regex(/^0x[0-9a-fA-F]+$/, "payload_hash must be a 0x-prefixed hex string"),
|
|
855
|
+
resource_url: z$1.string().url(),
|
|
856
|
+
merchant_to: z$1.string().min(1),
|
|
857
|
+
amount: z$1.string().min(1),
|
|
858
|
+
asset: z$1.string().min(1),
|
|
859
|
+
network: z$1.string().min(1),
|
|
782
860
|
// Required: expires_at is folded into the Haven-signed binding message, so the
|
|
783
861
|
// signer must receive the exact same value to reconstruct a matching message.
|
|
784
862
|
// Omitting it used to fail downstream with a cryptic "authentication message is
|
|
785
863
|
// invalid" — making it required surfaces a clear INVALID_INPUT at the boundary.
|
|
786
|
-
expires_at: z.string().min(1),
|
|
864
|
+
expires_at: z$1.string().min(1),
|
|
787
865
|
// #1138: present on a delegation-rail (v2) context. It commits to the EIP-712
|
|
788
866
|
// typed data the account validates; the signer refuses to raw-sign the bare
|
|
789
867
|
// hash when it is present, and refuses to sign typed data when it is absent.
|
|
790
|
-
typed_data_hash: z.string().regex(/^0x[0-9a-fA-F]+$/, "typed_data_hash must be a 0x-prefixed hex string").optional(),
|
|
868
|
+
typed_data_hash: z$1.string().regex(/^0x[0-9a-fA-F]+$/, "typed_data_hash must be a 0x-prefixed hex string").optional(),
|
|
791
869
|
// #1690: present on a v3 context. The delegate this quote was created FOR —
|
|
792
870
|
// the signer refuses to sign when it is not its own. Inside the Haven-signed
|
|
793
871
|
// message, so stripping it here would only break the binding signature.
|
|
794
|
-
payer_delegate: z.string().regex(/^0x[0-9a-fA-F]{40}$/, "payer_delegate must be a 0x-prefixed address").optional(),
|
|
795
|
-
payer_agent_id: z.string().min(1).optional(),
|
|
796
|
-
auth: z.object({
|
|
872
|
+
payer_delegate: z$1.string().regex(/^0x[0-9a-fA-F]{40}$/, "payer_delegate must be a 0x-prefixed address").optional(),
|
|
873
|
+
payer_agent_id: z$1.string().min(1).optional(),
|
|
874
|
+
auth: z$1.object({
|
|
797
875
|
// Open at the boundary, enforced in the signer — see bindingVersionSchema.
|
|
798
876
|
version: bindingVersionSchema,
|
|
799
|
-
message: z.string().min(1),
|
|
800
|
-
signature: z.string().regex(/^0x[0-9a-fA-F]+$/, "signature must be a 0x-prefixed hex string"),
|
|
801
|
-
signer: z.string().min(1)
|
|
877
|
+
message: z$1.string().min(1),
|
|
878
|
+
signature: z$1.string().regex(/^0x[0-9a-fA-F]+$/, "signature must be a 0x-prefixed hex string"),
|
|
879
|
+
signer: z$1.string().min(1)
|
|
802
880
|
})
|
|
803
881
|
};
|
|
804
|
-
var x402ExpectedSchema = z.object(x402ExpectedShape);
|
|
882
|
+
var x402ExpectedSchema = z$1.object(x402ExpectedShape);
|
|
805
883
|
var toolSchemas = {
|
|
806
884
|
haven_sign_sweep_delegate: {
|
|
807
885
|
// The authorization fields prepared by Haven's POST /sweep/prepare. Passed
|
|
@@ -816,17 +894,17 @@ var toolSchemas = {
|
|
|
816
894
|
// payload + expected context from Haven itself (authenticated with the
|
|
817
895
|
// locally-stored agent credential), so no bulky bytes ever cross the
|
|
818
896
|
// model. Pass payment_id ALONE for a delegation-rail x402 funding intent.
|
|
819
|
-
payment_id: z.string().min(1).optional(),
|
|
897
|
+
payment_id: z$1.string().min(1).optional(),
|
|
820
898
|
// The unsigned hash from haven_pay / haven_pay_x402_quote (payload_hash).
|
|
821
899
|
// Optional when payment_id is supplied (the fetch carries it).
|
|
822
|
-
payload_hash: z.string().regex(/^0x[0-9a-fA-F]+$/, "payload_hash must be a 0x-prefixed hex string").optional(),
|
|
900
|
+
payload_hash: z$1.string().regex(/^0x[0-9a-fA-F]+$/, "payload_hash must be a 0x-prefixed hex string").optional(),
|
|
823
901
|
// Pass x402.expected from hosted haven_pay_x402_quote when this hash funds
|
|
824
902
|
// a standard x402 merchant retry. The signer records it locally and returns
|
|
825
903
|
// an opaque x402_binding for the later header-signing step.
|
|
826
904
|
x402_expected: x402ExpectedSchema.optional(),
|
|
827
905
|
// #1138: delegation-rail intents sign THIS, not payload_hash. Object-typed
|
|
828
906
|
// (not z.unknown()) so MCP clients embed it as JSON rather than a string.
|
|
829
|
-
typed_data: z.record(z.string(), z.unknown()).optional(),
|
|
907
|
+
typed_data: z$1.record(z$1.string(), z$1.unknown()).optional(),
|
|
830
908
|
// #1255: the same payload as ONE opaque base64 string, exactly as returned
|
|
831
909
|
// by the hosted tools. Preferred over typed_data when both are present —
|
|
832
910
|
// an agent re-emitting multi-KB nested JSON between tool calls is the
|
|
@@ -835,35 +913,36 @@ var toolSchemas = {
|
|
|
835
913
|
// Bounded: a realistic redemption payload is ~10KB encoded; 256KB is
|
|
836
914
|
// generous headroom while keeping the offline signer from materializing
|
|
837
915
|
// arbitrarily large caller input.
|
|
838
|
-
typed_data_b64: z.string().min(1).max(262144).optional()
|
|
916
|
+
typed_data_b64: z$1.string().min(1).max(262144).optional()
|
|
839
917
|
},
|
|
840
918
|
haven_x402_sign_header: {
|
|
841
919
|
// The parsed HTTP 402 PaymentRequired from the merchant. Typed as an object
|
|
842
920
|
// (not z.unknown(), which becomes empty JSON Schema `{}`) so MCP clients
|
|
843
921
|
// embed it as JSON rather than serialising the object to a string.
|
|
844
|
-
payment_required: z.record(z.string(), z.unknown()),
|
|
922
|
+
payment_required: z$1.record(z$1.string(), z$1.unknown()),
|
|
845
923
|
// Opaque binding returned by haven_sign when x402_expected was supplied.
|
|
846
|
-
x402_binding: z.string().min(1)
|
|
924
|
+
x402_binding: z$1.string().min(1)
|
|
847
925
|
},
|
|
848
926
|
haven_sign_x402: {
|
|
849
927
|
// #1263: preferred — pass payment_id (plus payment_required) and the
|
|
850
928
|
// signer fetches the exact signing payload + expected context itself.
|
|
851
|
-
payment_id: z.string().min(1).optional(),
|
|
929
|
+
payment_id: z$1.string().min(1).optional(),
|
|
852
930
|
// One-shot x402 signing: funding hash + merchant header in one local call.
|
|
853
931
|
// Both optional when payment_id is supplied (the fetch carries them).
|
|
854
|
-
payload_hash: z.string().regex(/^0x[0-9a-fA-F]+$/, "payload_hash must be a 0x-prefixed hex string").optional(),
|
|
932
|
+
payload_hash: z$1.string().regex(/^0x[0-9a-fA-F]+$/, "payload_hash must be a 0x-prefixed hex string").optional(),
|
|
855
933
|
x402_expected: x402ExpectedSchema.optional(),
|
|
856
934
|
// #1355: optional when payment_id is supplied — the signer's context fetch
|
|
857
935
|
// carries the stored 402 PaymentRequired, so `{ payment_id }` alone is the
|
|
858
936
|
// preferred call. Still required (via the runtime check in the handler)
|
|
859
937
|
// for the quote-based fallback path without a payment_id.
|
|
860
|
-
payment_required: z.record(z.string(), z.unknown()).optional(),
|
|
938
|
+
payment_required: z$1.record(z$1.string(), z$1.unknown()).optional(),
|
|
861
939
|
// #1138: delegation-rail intents sign THIS, not payload_hash. Object-typed
|
|
862
940
|
// (not z.unknown()) so MCP clients embed it as JSON rather than a string.
|
|
863
|
-
typed_data: z.record(z.string(), z.unknown()).optional(),
|
|
941
|
+
typed_data: z$1.record(z$1.string(), z$1.unknown()).optional(),
|
|
864
942
|
// #1255: see haven_sign.typed_data_b64 — the copy-through-safe form.
|
|
865
|
-
typed_data_b64: z.string().min(1).max(262144).optional()
|
|
943
|
+
typed_data_b64: z$1.string().min(1).max(262144).optional()
|
|
866
944
|
}
|
|
945
|
+
// #3101: keys survive on the type (see the hosted server's contracts.ts).
|
|
867
946
|
};
|
|
868
947
|
var SIGN_DESCRIPTION = [
|
|
869
948
|
"Sign an unsigned Haven payment hash with the local delegate key. The delegate key never leaves",
|
|
@@ -961,7 +1040,7 @@ function toExpectedX402(raw) {
|
|
|
961
1040
|
};
|
|
962
1041
|
}
|
|
963
1042
|
function parseFetchedExpected(fetched) {
|
|
964
|
-
const parsed = z.object(x402ExpectedShape).safeParse(fetched.x402Expected);
|
|
1043
|
+
const parsed = z$1.object(x402ExpectedShape).safeParse(fetched.x402Expected);
|
|
965
1044
|
if (!parsed.success) {
|
|
966
1045
|
throw new HavenSigningError(
|
|
967
1046
|
`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"}`
|
|
@@ -1113,7 +1192,7 @@ function createToolHandlers(signer, options = {}) {
|
|
|
1113
1192
|
}
|
|
1114
1193
|
}
|
|
1115
1194
|
function parse(name, input) {
|
|
1116
|
-
return z.object(toolSchemas[name]).parse(input ?? {});
|
|
1195
|
+
return z$1.object(toolSchemas[name]).parse(input ?? {});
|
|
1117
1196
|
}
|
|
1118
1197
|
function coerceX402Expected(input) {
|
|
1119
1198
|
if (!input || typeof input !== "object") return input;
|
|
@@ -1152,7 +1231,7 @@ async function runTool(fn) {
|
|
|
1152
1231
|
}
|
|
1153
1232
|
}
|
|
1154
1233
|
function normalizeError(err) {
|
|
1155
|
-
if (err instanceof z.ZodError) {
|
|
1234
|
+
if (err instanceof z$1.ZodError) {
|
|
1156
1235
|
return {
|
|
1157
1236
|
success: false,
|
|
1158
1237
|
code: "INVALID_INPUT",
|
|
@@ -1168,7 +1247,13 @@ function normalizeError(err) {
|
|
|
1168
1247
|
supported_versions: [...err.supportedVersions],
|
|
1169
1248
|
received_version: err.receivedVersion,
|
|
1170
1249
|
fallback: err.fallback,
|
|
1171
|
-
next_action: AgentPaymentNextAction.StopAndTellUser
|
|
1250
|
+
next_action: AgentPaymentNextAction.StopAndTellUser,
|
|
1251
|
+
// #3103: no tool can fix a version skew from inside the call.
|
|
1252
|
+
...nextStepWireFields(signerRefusalStep({
|
|
1253
|
+
nextAction: AgentPaymentNextAction.StopAndTellUser,
|
|
1254
|
+
nextTool: null,
|
|
1255
|
+
nextToolOmittedReason: "update @haven_ai/signer by re-running the connector, then repeat the same call"
|
|
1256
|
+
}))
|
|
1172
1257
|
};
|
|
1173
1258
|
}
|
|
1174
1259
|
if (err instanceof HavenSignContextError) {
|
|
@@ -1180,7 +1265,14 @@ function normalizeError(err) {
|
|
|
1180
1265
|
...err.fallback !== void 0 ? { fallback: err.fallback } : {},
|
|
1181
1266
|
...err.retry_with_new_quote ? { retry_with_new_quote: true } : {},
|
|
1182
1267
|
...err.http_status !== void 0 ? { http_status: err.http_status } : {},
|
|
1183
|
-
...err.backend_error_code !== void 0 ? { backend_error_code: err.backend_error_code } : {}
|
|
1268
|
+
...err.backend_error_code !== void 0 ? { backend_error_code: err.backend_error_code } : {},
|
|
1269
|
+
// #3103: the typed step the error decided beside its action.
|
|
1270
|
+
...err.next_tool ? { next_tool: err.next_tool } : {},
|
|
1271
|
+
...err.next_tool_server ? { next_tool_server: err.next_tool_server } : {},
|
|
1272
|
+
...err.next_tool_name ? { next_tool_name: err.next_tool_name } : {},
|
|
1273
|
+
...err.next_tool_server_role ? { next_tool_server_role: err.next_tool_server_role } : {},
|
|
1274
|
+
...err.next_arguments ? { next_arguments: err.next_arguments } : {},
|
|
1275
|
+
...err.next_tool_omitted_reason ? { next_tool_omitted_reason: err.next_tool_omitted_reason } : {}
|
|
1184
1276
|
};
|
|
1185
1277
|
}
|
|
1186
1278
|
if (err instanceof HavenSigningError) {
|
|
@@ -1199,7 +1291,13 @@ function normalizeError(err) {
|
|
|
1199
1291
|
...err.code === AgentPaymentFailureCode.PaymentWindowExpired ? {
|
|
1200
1292
|
next_action: AgentPaymentNextAction.PaymentWindowExpired,
|
|
1201
1293
|
retry_with_new_quote: true,
|
|
1202
|
-
suggested_tool: "haven_pay_mcp_tool"
|
|
1294
|
+
suggested_tool: "haven_pay_mcp_tool",
|
|
1295
|
+
// #3103: same omission as the hosted window-expired helper.
|
|
1296
|
+
...nextStepWireFields(signerRefusalStep({
|
|
1297
|
+
nextAction: AgentPaymentNextAction.PaymentWindowExpired,
|
|
1298
|
+
nextTool: null,
|
|
1299
|
+
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)"
|
|
1300
|
+
}))
|
|
1203
1301
|
} : {}
|
|
1204
1302
|
};
|
|
1205
1303
|
}
|
|
@@ -1329,7 +1427,7 @@ async function writeAckFile(path, hash) {
|
|
|
1329
1427
|
);
|
|
1330
1428
|
}
|
|
1331
1429
|
var SIGNER_NAME = "@haven_ai/signer";
|
|
1332
|
-
var SIGNER_VERSION = "0.
|
|
1430
|
+
var SIGNER_VERSION = "0.4.0-alpha.0";
|
|
1333
1431
|
async function resolveEdgeSigner(options = {}) {
|
|
1334
1432
|
const { signer } = await resolveSignerRuntime(options);
|
|
1335
1433
|
return signer;
|
|
@@ -1439,6 +1537,6 @@ function toMcpResult(payload) {
|
|
|
1439
1537
|
};
|
|
1440
1538
|
}
|
|
1441
1539
|
|
|
1442
|
-
export { SIGNER_ACK_ENV, SIGNER_CAPABILITY_KEY, SIGNER_NAME, SIGNER_VERSION, SUPPORTED_SWEEP_BINDING_VERSIONS, SUPPORTED_X402_EXPECTED_VERSIONS, appendSigningAuditEntry, assertSupportedBindingVersion, assertSupportedNodeVersion, assertX402MatchesExpected, buildSignerMcpServer, computeSignerConsentHash, createEdgeSigner, createSigningAuditEntry, createToolHandlers, defaultSigningAuditPath, ensureSignerConsent, hashPayloadForAudit, loadSignerCredentials, readAccountAddressEnv, readAccountAddressField, renderSignerConsentBlock, resolveEdgeSigner, resolveSignerRuntime, runSignerConsentGate, runSignerStdioServer, signerCapabilityAdvertisement, signerCompatibility, signerInstructions, toolDescriptions, toolSchemas, warnIfCredentialFilePermissive };
|
|
1540
|
+
export { SIGNER_ACK_ENV, SIGNER_CAPABILITY_KEY, SIGNER_HOSTED_HANDOFF_SHAPES, SIGNER_NAME, SIGNER_VERSION, SUPPORTED_SWEEP_BINDING_VERSIONS, SUPPORTED_X402_EXPECTED_VERSIONS, appendSigningAuditEntry, assertSupportedBindingVersion, assertSupportedNodeVersion, assertX402MatchesExpected, buildSignerMcpServer, computeSignerConsentHash, createEdgeSigner, createSigningAuditEntry, createToolHandlers, defaultSigningAuditPath, ensureSignerConsent, hashPayloadForAudit, loadSignerCredentials, readAccountAddressEnv, readAccountAddressField, renderSignerConsentBlock, resolveEdgeSigner, resolveSignerRuntime, runSignerConsentGate, runSignerStdioServer, signerCapabilityAdvertisement, signerCompatibility, signerInstructions, toolDescriptions, toolSchemas, warnIfCredentialFilePermissive };
|
|
1443
1541
|
//# sourceMappingURL=index.js.map
|
|
1444
1542
|
//# sourceMappingURL=index.js.map
|