@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.cjs
CHANGED
|
@@ -11,6 +11,7 @@ var stdio_js = require('@modelcontextprotocol/sdk/server/stdio.js');
|
|
|
11
11
|
var os = require('os');
|
|
12
12
|
var path = require('path');
|
|
13
13
|
var v3 = require('zod/v3');
|
|
14
|
+
var zod = require('zod');
|
|
14
15
|
|
|
15
16
|
// src/core.ts
|
|
16
17
|
var DELEGATION_MANAGER = "0xdb9B1e94B5b69Df7e401DDbedE43491141047dB3";
|
|
@@ -496,7 +497,10 @@ function signerInstructions() {
|
|
|
496
497
|
"",
|
|
497
498
|
"A version-mismatch refusal from haven_sign / haven_sign_x402 / haven_sign_sweep_delegate is",
|
|
498
499
|
"machine-readable, not just prose: it carries code, supported_versions, received_version, and",
|
|
499
|
-
"fallback fields alongside the message, so you can branch on it directly."
|
|
500
|
+
"fallback fields alongside the message, so you can branch on it directly. Every signer",
|
|
501
|
+
"refusal also carries the next-step family: next_tool_name + next_tool_server_role when a",
|
|
502
|
+
"hosted tool follows (resolve the role against your own server names), else",
|
|
503
|
+
"next_tool_omitted_reason saying why not."
|
|
500
504
|
].join("\n");
|
|
501
505
|
}
|
|
502
506
|
async function loadSignerCredentials(path = process.env.HAVEN_CREDENTIALS) {
|
|
@@ -642,6 +646,38 @@ function stableStringify(value) {
|
|
|
642
646
|
const object = value;
|
|
643
647
|
return `{${Object.keys(object).sort().map((key) => `${JSON.stringify(key)}:${stableStringify(object[key])}`).join(",")}}`;
|
|
644
648
|
}
|
|
649
|
+
var SIGNER_HOSTED_HANDOFF_SHAPES = {
|
|
650
|
+
haven_get_payment_status: { payment_id: zod.z.string().min(1) }
|
|
651
|
+
};
|
|
652
|
+
function target(shape) {
|
|
653
|
+
const schema = zod.z.object(shape).strict();
|
|
654
|
+
return {
|
|
655
|
+
role: "hosted",
|
|
656
|
+
validate: (input) => {
|
|
657
|
+
const r = schema.safeParse(input ?? {});
|
|
658
|
+
return r.success ? null : r.error.errors.map((e) => `${e.path.join(".") || "(root)"}: ${e.message}`).join("; ");
|
|
659
|
+
}
|
|
660
|
+
};
|
|
661
|
+
}
|
|
662
|
+
var SIGNER_NEXT_STEP_TARGETS = {
|
|
663
|
+
haven_get_payment_status: target(SIGNER_HOSTED_HANDOFF_SHAPES.haven_get_payment_status)
|
|
664
|
+
};
|
|
665
|
+
var nextStep = sdk.createNextStepBuilder(SIGNER_NEXT_STEP_TARGETS);
|
|
666
|
+
function signerRefusalStep(input) {
|
|
667
|
+
return nextStep({ ...input, safeToContinue: false, reason: "" });
|
|
668
|
+
}
|
|
669
|
+
function nextStepWireFields(step) {
|
|
670
|
+
return {
|
|
671
|
+
...step.next_tool ? { next_tool: step.next_tool } : {},
|
|
672
|
+
...step.next_tool_server ? { next_tool_server: step.next_tool_server } : {},
|
|
673
|
+
...step.next_tool_name ? { next_tool_name: step.next_tool_name } : {},
|
|
674
|
+
...step.next_tool_server_role ? { next_tool_server_role: step.next_tool_server_role } : {},
|
|
675
|
+
...step.next_arguments ? { next_arguments: step.next_arguments } : {},
|
|
676
|
+
...step.next_tool_omitted_reason ? { next_tool_omitted_reason: step.next_tool_omitted_reason } : {}
|
|
677
|
+
};
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
// src/sign-context.ts
|
|
645
681
|
var HavenSignContextError = class extends sdk.HavenSigningError {
|
|
646
682
|
/**
|
|
647
683
|
* #3001 / #3010 review: the recovery path, when signing OTHER bytes is
|
|
@@ -661,28 +697,63 @@ var HavenSignContextError = class extends sdk.HavenSigningError {
|
|
|
661
697
|
* plain `HavenError` branch already does for that code.
|
|
662
698
|
*/
|
|
663
699
|
next_action;
|
|
700
|
+
/**
|
|
701
|
+
* #3103 (epic #3105, decisions 1 and 3): the typed next step beside the
|
|
702
|
+
* action. A refusal Haven made (`SIGN_CONTEXT_REFUSED`, not expired) names
|
|
703
|
+
* the hosted status read with the payment id; a transport failure or a
|
|
704
|
+
* malformed body names no tool — the remedy is re-running the SAME quote
|
|
705
|
+
* tool with `include_signing_payload: true` — and an expired window names
|
|
706
|
+
* none either (which quote tool depends on the flow). Never null: a step
|
|
707
|
+
* with no tool carries `next_tool_omitted_reason`. Additive: the class is
|
|
708
|
+
* not exported from the package index — the published surface is the
|
|
709
|
+
* `ToolFailure` envelope `tools.ts` builds from these fields — and every
|
|
710
|
+
* constructor call site is in this file.
|
|
711
|
+
*/
|
|
712
|
+
next_tool;
|
|
713
|
+
next_tool_server;
|
|
714
|
+
next_tool_name;
|
|
715
|
+
next_tool_server_role;
|
|
716
|
+
next_arguments;
|
|
717
|
+
next_tool_omitted_reason;
|
|
664
718
|
retry_with_new_quote;
|
|
665
719
|
/** Present only for `SIGN_CONTEXT_REFUSED` (the backend's HTTP status). */
|
|
666
720
|
http_status;
|
|
667
721
|
/** Present only for `SIGN_CONTEXT_REFUSED`: the backend's own `error_code`. */
|
|
668
722
|
backend_error_code;
|
|
669
|
-
constructor(message, code, refusal) {
|
|
723
|
+
constructor(message, code, refusal, paymentId) {
|
|
670
724
|
super(message);
|
|
671
725
|
this.code = code;
|
|
672
726
|
this.name = "HavenSignContextError";
|
|
727
|
+
let step;
|
|
673
728
|
if (code === "SIGN_CONTEXT_REFUSED") {
|
|
674
729
|
this.http_status = refusal?.httpStatus;
|
|
675
730
|
this.backend_error_code = refusal?.errorCode;
|
|
676
731
|
if (refusal?.httpStatus === 410 || refusal?.errorCode === "expired") {
|
|
677
732
|
this.next_action = sdk.AgentPaymentNextAction.PaymentWindowExpired;
|
|
678
733
|
this.retry_with_new_quote = true;
|
|
734
|
+
step = signerRefusalStep({
|
|
735
|
+
nextAction: sdk.AgentPaymentNextAction.PaymentWindowExpired,
|
|
736
|
+
nextTool: null,
|
|
737
|
+
nextToolOmittedReason: "re-run the hosted quote tool you called with the same idempotency_key; which one depends on the flow"
|
|
738
|
+
});
|
|
679
739
|
} else {
|
|
680
740
|
this.next_action = sdk.AgentPaymentNextAction.StopAndTellUser;
|
|
741
|
+
step = signerRefusalStep({
|
|
742
|
+
nextAction: sdk.AgentPaymentNextAction.StopAndTellUser,
|
|
743
|
+
nextTool: "haven_get_payment_status",
|
|
744
|
+
nextArguments: { payment_id: paymentId }
|
|
745
|
+
});
|
|
681
746
|
}
|
|
682
747
|
} else {
|
|
683
748
|
this.fallback = "typed_data_b64";
|
|
684
749
|
this.next_action = sdk.AgentPaymentNextAction.StopAndTellUser;
|
|
750
|
+
step = signerRefusalStep({
|
|
751
|
+
nextAction: sdk.AgentPaymentNextAction.StopAndTellUser,
|
|
752
|
+
nextTool: null,
|
|
753
|
+
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"
|
|
754
|
+
});
|
|
685
755
|
}
|
|
756
|
+
Object.assign(this, nextStepWireFields(step));
|
|
686
757
|
}
|
|
687
758
|
};
|
|
688
759
|
async function loadHavenIdentity(credentialsPath) {
|
|
@@ -714,7 +785,9 @@ async function fetchX402SignContext(identity, paymentId, fetchImpl = fetch, time
|
|
|
714
785
|
const timedOut = err instanceof Error && (err.name === "TimeoutError" || err.name === "AbortError");
|
|
715
786
|
throw new HavenSignContextError(
|
|
716
787
|
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.`,
|
|
717
|
-
timedOut ? "SIGN_CONTEXT_TIMEOUT" : "SIGN_CONTEXT_UNREACHABLE"
|
|
788
|
+
timedOut ? "SIGN_CONTEXT_TIMEOUT" : "SIGN_CONTEXT_UNREACHABLE",
|
|
789
|
+
void 0,
|
|
790
|
+
paymentId
|
|
718
791
|
);
|
|
719
792
|
}
|
|
720
793
|
let body;
|
|
@@ -724,7 +797,9 @@ async function fetchX402SignContext(identity, paymentId, fetchImpl = fetch, time
|
|
|
724
797
|
if (err instanceof Error && (err.name === "TimeoutError" || err.name === "AbortError")) {
|
|
725
798
|
throw new HavenSignContextError(
|
|
726
799
|
`Haven did not finish sending the signing context for ${paymentId} within ${timeoutMs} ms. Retry, or pass typed_data_b64 from the quote result instead.`,
|
|
727
|
-
"SIGN_CONTEXT_TIMEOUT"
|
|
800
|
+
"SIGN_CONTEXT_TIMEOUT",
|
|
801
|
+
void 0,
|
|
802
|
+
paymentId
|
|
728
803
|
);
|
|
729
804
|
}
|
|
730
805
|
body = {};
|
|
@@ -734,7 +809,8 @@ async function fetchX402SignContext(identity, paymentId, fetchImpl = fetch, time
|
|
|
734
809
|
throw new HavenSignContextError(
|
|
735
810
|
`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." : ""),
|
|
736
811
|
"SIGN_CONTEXT_REFUSED",
|
|
737
|
-
{ httpStatus: response.status, errorCode: typeof body.error_code === "string" ? body.error_code : void 0 }
|
|
812
|
+
{ httpStatus: response.status, errorCode: typeof body.error_code === "string" ? body.error_code : void 0 },
|
|
813
|
+
paymentId
|
|
738
814
|
);
|
|
739
815
|
}
|
|
740
816
|
const signData = body.sign_data;
|
|
@@ -742,7 +818,9 @@ async function fetchX402SignContext(identity, paymentId, fetchImpl = fetch, time
|
|
|
742
818
|
if (!signData || typeof signData.hash !== "string" || !signData.typed_data || typeof signData.typed_data !== "object" || !x402Expected) {
|
|
743
819
|
throw new HavenSignContextError(
|
|
744
820
|
"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.",
|
|
745
|
-
"SIGN_CONTEXT_MALFORMED"
|
|
821
|
+
"SIGN_CONTEXT_MALFORMED",
|
|
822
|
+
void 0,
|
|
823
|
+
paymentId
|
|
746
824
|
);
|
|
747
825
|
}
|
|
748
826
|
const paymentRequired = body.payment_required;
|
|
@@ -866,6 +944,7 @@ var toolSchemas = {
|
|
|
866
944
|
// #1255: see haven_sign.typed_data_b64 — the copy-through-safe form.
|
|
867
945
|
typed_data_b64: v3.z.string().min(1).max(262144).optional()
|
|
868
946
|
}
|
|
947
|
+
// #3101: keys survive on the type (see the hosted server's contracts.ts).
|
|
869
948
|
};
|
|
870
949
|
var SIGN_DESCRIPTION = [
|
|
871
950
|
"Sign an unsigned Haven payment hash with the local delegate key. The delegate key never leaves",
|
|
@@ -1170,7 +1249,13 @@ function normalizeError(err) {
|
|
|
1170
1249
|
supported_versions: [...err.supportedVersions],
|
|
1171
1250
|
received_version: err.receivedVersion,
|
|
1172
1251
|
fallback: err.fallback,
|
|
1173
|
-
next_action: sdk.AgentPaymentNextAction.StopAndTellUser
|
|
1252
|
+
next_action: sdk.AgentPaymentNextAction.StopAndTellUser,
|
|
1253
|
+
// #3103: no tool can fix a version skew from inside the call.
|
|
1254
|
+
...nextStepWireFields(signerRefusalStep({
|
|
1255
|
+
nextAction: sdk.AgentPaymentNextAction.StopAndTellUser,
|
|
1256
|
+
nextTool: null,
|
|
1257
|
+
nextToolOmittedReason: "update @haven_ai/signer by re-running the connector, then repeat the same call"
|
|
1258
|
+
}))
|
|
1174
1259
|
};
|
|
1175
1260
|
}
|
|
1176
1261
|
if (err instanceof HavenSignContextError) {
|
|
@@ -1182,7 +1267,14 @@ function normalizeError(err) {
|
|
|
1182
1267
|
...err.fallback !== void 0 ? { fallback: err.fallback } : {},
|
|
1183
1268
|
...err.retry_with_new_quote ? { retry_with_new_quote: true } : {},
|
|
1184
1269
|
...err.http_status !== void 0 ? { http_status: err.http_status } : {},
|
|
1185
|
-
...err.backend_error_code !== void 0 ? { backend_error_code: err.backend_error_code } : {}
|
|
1270
|
+
...err.backend_error_code !== void 0 ? { backend_error_code: err.backend_error_code } : {},
|
|
1271
|
+
// #3103: the typed step the error decided beside its action.
|
|
1272
|
+
...err.next_tool ? { next_tool: err.next_tool } : {},
|
|
1273
|
+
...err.next_tool_server ? { next_tool_server: err.next_tool_server } : {},
|
|
1274
|
+
...err.next_tool_name ? { next_tool_name: err.next_tool_name } : {},
|
|
1275
|
+
...err.next_tool_server_role ? { next_tool_server_role: err.next_tool_server_role } : {},
|
|
1276
|
+
...err.next_arguments ? { next_arguments: err.next_arguments } : {},
|
|
1277
|
+
...err.next_tool_omitted_reason ? { next_tool_omitted_reason: err.next_tool_omitted_reason } : {}
|
|
1186
1278
|
};
|
|
1187
1279
|
}
|
|
1188
1280
|
if (err instanceof sdk.HavenSigningError) {
|
|
@@ -1201,7 +1293,13 @@ function normalizeError(err) {
|
|
|
1201
1293
|
...err.code === sdk.AgentPaymentFailureCode.PaymentWindowExpired ? {
|
|
1202
1294
|
next_action: sdk.AgentPaymentNextAction.PaymentWindowExpired,
|
|
1203
1295
|
retry_with_new_quote: true,
|
|
1204
|
-
suggested_tool: "haven_pay_mcp_tool"
|
|
1296
|
+
suggested_tool: "haven_pay_mcp_tool",
|
|
1297
|
+
// #3103: same omission as the hosted window-expired helper.
|
|
1298
|
+
...nextStepWireFields(signerRefusalStep({
|
|
1299
|
+
nextAction: sdk.AgentPaymentNextAction.PaymentWindowExpired,
|
|
1300
|
+
nextTool: null,
|
|
1301
|
+
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)"
|
|
1302
|
+
}))
|
|
1205
1303
|
} : {}
|
|
1206
1304
|
};
|
|
1207
1305
|
}
|
|
@@ -1331,7 +1429,7 @@ async function writeAckFile(path$1, hash) {
|
|
|
1331
1429
|
);
|
|
1332
1430
|
}
|
|
1333
1431
|
var SIGNER_NAME = "@haven_ai/signer";
|
|
1334
|
-
var SIGNER_VERSION = "0.
|
|
1432
|
+
var SIGNER_VERSION = "0.4.0-alpha.0";
|
|
1335
1433
|
async function resolveEdgeSigner(options = {}) {
|
|
1336
1434
|
const { signer } = await resolveSignerRuntime(options);
|
|
1337
1435
|
return signer;
|
|
@@ -1443,6 +1541,7 @@ function toMcpResult(payload) {
|
|
|
1443
1541
|
|
|
1444
1542
|
exports.SIGNER_ACK_ENV = SIGNER_ACK_ENV;
|
|
1445
1543
|
exports.SIGNER_CAPABILITY_KEY = SIGNER_CAPABILITY_KEY;
|
|
1544
|
+
exports.SIGNER_HOSTED_HANDOFF_SHAPES = SIGNER_HOSTED_HANDOFF_SHAPES;
|
|
1446
1545
|
exports.SIGNER_NAME = SIGNER_NAME;
|
|
1447
1546
|
exports.SIGNER_VERSION = SIGNER_VERSION;
|
|
1448
1547
|
exports.SUPPORTED_SWEEP_BINDING_VERSIONS = SUPPORTED_SWEEP_BINDING_VERSIONS;
|