@haven_ai/signer 0.6.0-alpha.0 → 0.7.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 +7 -3
- package/dist/cli.cjs +267 -20
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +268 -21
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +268 -20
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +31 -4
- package/dist/index.d.ts +31 -4
- package/dist/index.js +269 -22
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -255,7 +255,7 @@ function assertSupportedBindingVersion(received, supported, context) {
|
|
|
255
255
|
const highest = Math.max(...supported);
|
|
256
256
|
const outOfDate = received > highest;
|
|
257
257
|
const code = context === "x402 expected context" ? edge.SignerRefusalCode.UnsupportedExpectedContextVersion : edge.SignerRefusalCode.UnsupportedSweepBindingVersion;
|
|
258
|
-
const ceiling = outOfDate ? `This signer is out of date: it supports ${context} versions up to ${highest}, and Haven sent version ${received}. Update @haven_ai/signer \u2014
|
|
258
|
+
const ceiling = outOfDate ? `This signer is out of date: it supports ${context} versions up to ${highest}, and Haven sent version ${received}. Update @haven_ai/signer \u2014 run \`${edge.connectorUpgradeCommand()}\`, then the repair line it prints, which reinstalls the pinned MCP runtime.` : `Unsupported ${context} version ${received}: this signer supports ${supported.join(", ")}.`;
|
|
259
259
|
const fallback = outOfDate ? edge.SIGNER_UPDATE_FALLBACK : `This ${context} version (${received}) is older than what this signer enforces (${supported.join(", ")}) \u2014 updating @haven_ai/signer will not restore it. Nothing was signed or spent; stop and tell the user rather than retrying.`;
|
|
260
260
|
throw new edge.HavenUnsupportedSignerVersionError(
|
|
261
261
|
`${ceiling} Nothing was signed. Do not rewrite the version field to a supported value: it is part of the Haven-signed binding message, so changing it invalidates the signature and would misrepresent what Haven declared.`,
|
|
@@ -434,7 +434,7 @@ var HavenSignContextError = class extends edge.HavenSigningError {
|
|
|
434
434
|
/**
|
|
435
435
|
* #3303: on a 426 `client_outdated` refusal, the backend's update hint —
|
|
436
436
|
* this signer is below the minimum the deployment set, and
|
|
437
|
-
* `client_update.upgrade_command`
|
|
437
|
+
* `client_update.upgrade_command` starts the update (#3412: for the signer, the doctor, then its repair line).
|
|
438
438
|
*/
|
|
439
439
|
client_update;
|
|
440
440
|
constructor(message, code, refusal, paymentId, flow = "x402") {
|
|
@@ -451,7 +451,7 @@ var HavenSignContextError = class extends edge.HavenSigningError {
|
|
|
451
451
|
step = signerRefusalStep({
|
|
452
452
|
nextAction: edge.AgentPaymentNextAction.StopAndTellUser,
|
|
453
453
|
nextTool: null,
|
|
454
|
-
nextToolOmittedReason: `this signer is below the minimum version Haven accepts \u2014 tell the user to run ${refusal.clientUpdate?.upgrade_command ??
|
|
454
|
+
nextToolOmittedReason: `this signer is below the minimum version Haven accepts \u2014 tell the user to run ${refusal.clientUpdate?.upgrade_command ?? edge.connectorUpgradeCommand()} and the repair line it prints, restart the agent runtime, then retry the same haven_sign / haven_sign_x402 call`
|
|
455
455
|
});
|
|
456
456
|
} else if (flow === "direct" && refusal?.httpStatus === 404) {
|
|
457
457
|
this.fallback = "typed_data_b64";
|
|
@@ -666,7 +666,120 @@ async function fetchDirectSignContext(identity, paymentId, fetchImpl = fetch, ti
|
|
|
666
666
|
clientUpdate: edge.readClientUpdate(body.client_update)
|
|
667
667
|
};
|
|
668
668
|
}
|
|
669
|
-
var SUPPORTED_TASK_SIGN_CONTEXT_VERSIONS = [
|
|
669
|
+
var SUPPORTED_TASK_SIGN_CONTEXT_VERSIONS = [edge.TASK_SIGN_CONTEXT_VERSION];
|
|
670
|
+
var SUPPORTED_SUB_BUDGET_SIGN_CONTEXT_VERSIONS = [1];
|
|
671
|
+
async function fetchSubBudgetSignContext(identity, subBudgetId, fetchImpl = fetch, timeoutMs = SIGN_CONTEXT_TIMEOUT_MS, clientIdentity) {
|
|
672
|
+
let response;
|
|
673
|
+
try {
|
|
674
|
+
response = await fetchImpl(
|
|
675
|
+
`${identity.apiUrl}/sub-budgets/${encodeURIComponent(subBudgetId)}/sign-context`,
|
|
676
|
+
{
|
|
677
|
+
headers: signContextHeaders(identity, clientIdentity),
|
|
678
|
+
signal: AbortSignal.timeout(timeoutMs)
|
|
679
|
+
}
|
|
680
|
+
);
|
|
681
|
+
} catch (err) {
|
|
682
|
+
const timedOut = err instanceof Error && (err.name === "TimeoutError" || err.name === "AbortError");
|
|
683
|
+
throw new HavenSignContextError(
|
|
684
|
+
timedOut ? `Haven did not answer the sub-budget signing-context fetch for ${subBudgetId} within ${timeoutMs} ms. Retry.` : `Could not reach Haven to fetch the sub-budget signing context for ${subBudgetId}: ${err instanceof Error ? err.message : String(err)}.`,
|
|
685
|
+
timedOut ? "SIGN_CONTEXT_TIMEOUT" : "SIGN_CONTEXT_UNREACHABLE",
|
|
686
|
+
void 0,
|
|
687
|
+
subBudgetId,
|
|
688
|
+
"task"
|
|
689
|
+
);
|
|
690
|
+
}
|
|
691
|
+
let body;
|
|
692
|
+
try {
|
|
693
|
+
body = await response.json();
|
|
694
|
+
} catch (err) {
|
|
695
|
+
if (err instanceof Error && (err.name === "TimeoutError" || err.name === "AbortError")) {
|
|
696
|
+
throw new HavenSignContextError(
|
|
697
|
+
`Haven did not finish sending the sub-budget signing context for ${subBudgetId} within ${timeoutMs} ms. Retry.`,
|
|
698
|
+
"SIGN_CONTEXT_TIMEOUT",
|
|
699
|
+
void 0,
|
|
700
|
+
subBudgetId,
|
|
701
|
+
"task"
|
|
702
|
+
);
|
|
703
|
+
}
|
|
704
|
+
body = {};
|
|
705
|
+
}
|
|
706
|
+
if (!response.ok) {
|
|
707
|
+
const detail = typeof body.error === "string" ? body.error : `HTTP ${response.status}`;
|
|
708
|
+
throw new HavenSignContextError(
|
|
709
|
+
`Haven refused the sub-budget signing-context fetch for ${subBudgetId}: ${detail}`,
|
|
710
|
+
"SIGN_CONTEXT_REFUSED",
|
|
711
|
+
refusalOf(response.status, body),
|
|
712
|
+
subBudgetId,
|
|
713
|
+
"task"
|
|
714
|
+
);
|
|
715
|
+
}
|
|
716
|
+
const purpose = body.purpose;
|
|
717
|
+
const typedData = body.typed_data;
|
|
718
|
+
const version = body.sub_budget_sign_context_version;
|
|
719
|
+
const expected = body.expected;
|
|
720
|
+
const malformed = (detail) => {
|
|
721
|
+
throw new HavenSignContextError(
|
|
722
|
+
`The Haven sub-budget sign-context response for ${subBudgetId} is malformed: ${detail}.`,
|
|
723
|
+
"SIGN_CONTEXT_MALFORMED",
|
|
724
|
+
void 0,
|
|
725
|
+
subBudgetId,
|
|
726
|
+
"task"
|
|
727
|
+
);
|
|
728
|
+
};
|
|
729
|
+
if (typeof version !== "number" || !SUPPORTED_SUB_BUDGET_SIGN_CONTEXT_VERSIONS.includes(version)) {
|
|
730
|
+
throw new HavenSignContextError(
|
|
731
|
+
`The Haven sub-budget sign-context response is version ${String(version)}, which this signer does not support (supported: ${SUPPORTED_SUB_BUDGET_SIGN_CONTEXT_VERSIONS.join(", ")}). Update @haven_ai/signer.`,
|
|
732
|
+
"SIGN_CONTEXT_MALFORMED",
|
|
733
|
+
void 0,
|
|
734
|
+
subBudgetId,
|
|
735
|
+
"task"
|
|
736
|
+
);
|
|
737
|
+
}
|
|
738
|
+
if (!typedData || typeof typedData !== "object") return malformed("missing typed_data");
|
|
739
|
+
if (!expected || typeof expected !== "object") return malformed("missing expected");
|
|
740
|
+
const e = expected;
|
|
741
|
+
if (purpose === "open") {
|
|
742
|
+
if (typeof e.delegate_account !== "string" || typeof e.chain_id !== "number" || typeof e.token_address !== "string" || typeof e.period_amount_atomic !== "string" || typeof e.period_duration_seconds !== "number" || typeof e.start_date !== "number" || e.recipient_address !== null && typeof e.recipient_address !== "string" || typeof e.expires_at !== "number" || typeof e.parent_delegation_hash !== "string" || typeof e.child_delegate_account !== "string") {
|
|
743
|
+
return malformed("expected is missing a required field for purpose open");
|
|
744
|
+
}
|
|
745
|
+
return {
|
|
746
|
+
purpose: "open",
|
|
747
|
+
subBudgetId: String(body.sub_budget_id ?? subBudgetId),
|
|
748
|
+
typedData,
|
|
749
|
+
expected: {
|
|
750
|
+
delegateAccount: e.delegate_account,
|
|
751
|
+
chainId: e.chain_id,
|
|
752
|
+
tokenAddress: e.token_address,
|
|
753
|
+
periodAmountAtomic: e.period_amount_atomic,
|
|
754
|
+
periodDurationSeconds: e.period_duration_seconds,
|
|
755
|
+
startDate: e.start_date,
|
|
756
|
+
recipientAddress: e.recipient_address ?? null,
|
|
757
|
+
expiresAt: e.expires_at,
|
|
758
|
+
parentDelegationHash: e.parent_delegation_hash,
|
|
759
|
+
childDelegateAccount: e.child_delegate_account
|
|
760
|
+
}
|
|
761
|
+
};
|
|
762
|
+
}
|
|
763
|
+
if (purpose === "close") {
|
|
764
|
+
const userOpHash = body.user_op_hash;
|
|
765
|
+
if (typeof userOpHash !== "string") return malformed("missing user_op_hash for purpose close");
|
|
766
|
+
if (typeof e.delegate_account !== "string" || typeof e.chain_id !== "number" || typeof e.delegation_hash !== "string") {
|
|
767
|
+
return malformed("expected is missing a required field for purpose close");
|
|
768
|
+
}
|
|
769
|
+
return {
|
|
770
|
+
purpose: "close",
|
|
771
|
+
subBudgetId: String(body.sub_budget_id ?? subBudgetId),
|
|
772
|
+
typedData,
|
|
773
|
+
userOpHash,
|
|
774
|
+
expected: {
|
|
775
|
+
delegateAccount: e.delegate_account,
|
|
776
|
+
chainId: e.chain_id,
|
|
777
|
+
delegationHash: e.delegation_hash
|
|
778
|
+
}
|
|
779
|
+
};
|
|
780
|
+
}
|
|
781
|
+
return malformed(`unknown purpose '${String(purpose)}'`);
|
|
782
|
+
}
|
|
670
783
|
async function fetchTaskBudgetSignContext(identity, taskBudgetId, fetchImpl = fetch, timeoutMs = SIGN_CONTEXT_TIMEOUT_MS, clientIdentity) {
|
|
671
784
|
let response;
|
|
672
785
|
try {
|
|
@@ -808,8 +921,8 @@ function signerInstructions() {
|
|
|
808
921
|
"Haven quote and prepare results report the expected-context version they will emit",
|
|
809
922
|
"(signer_compatibility.x402_expected_context_version). If that version is not in the list",
|
|
810
923
|
"above, this signer is out of date: STOP before signing, and tell the user to update",
|
|
811
|
-
`@haven_ai/signer
|
|
812
|
-
"MCP runtime. Do not edit the version field to a supported value \u2014 it is part of the",
|
|
924
|
+
`@haven_ai/signer: run \`${edge.connectorUpgradeCommand()}\`, then the repair line it prints,`,
|
|
925
|
+
"which reinstalls the pinned MCP runtime. Do not edit the version field to a supported value \u2014 it is part of the",
|
|
813
926
|
"Haven-signed binding message, so changing it invalidates the signature.",
|
|
814
927
|
"",
|
|
815
928
|
"A version-mismatch refusal from haven_sign / haven_sign_x402 / haven_sign_sweep_delegate is",
|
|
@@ -817,7 +930,13 @@ function signerInstructions() {
|
|
|
817
930
|
"fallback fields alongside the message, so you can branch on it directly. Every signer",
|
|
818
931
|
"refusal also carries the next-step family: next_tool_name + next_tool_server_role when a",
|
|
819
932
|
"hosted tool follows (resolve the role against your own server names), else",
|
|
820
|
-
"next_tool_omitted_reason saying why not."
|
|
933
|
+
"next_tool_omitted_reason saying why not.",
|
|
934
|
+
"",
|
|
935
|
+
"An undeclared top-level argument is refused, not stripped: haven_sign answers",
|
|
936
|
+
"UNSUPPORTED_ARGUMENT with unknown_arguments, signer_version and fallback (the update",
|
|
937
|
+
"command) instead of dropping the key and answering the generic signing error \u2014 if you see",
|
|
938
|
+
"that refusal, this signer predates the argument form you sent. Update the signer; nothing",
|
|
939
|
+
"was signed, fetched or audited."
|
|
821
940
|
].join("\n");
|
|
822
941
|
}
|
|
823
942
|
var OWNER_ONLY_MODE = 384;
|
|
@@ -1036,6 +1155,9 @@ var HavenBareHashRefusedError = class extends edge.HavenSigningError {
|
|
|
1036
1155
|
};
|
|
1037
1156
|
|
|
1038
1157
|
// src/tools.ts
|
|
1158
|
+
var SIGNER_NAME = "@haven_ai/signer";
|
|
1159
|
+
var SIGNER_VERSION = "0.7.0-alpha.0";
|
|
1160
|
+
var UNSUPPORTED_ARGUMENT_MARKER = "HavenSignerUnsupportedArgument:";
|
|
1039
1161
|
var USEROP_BINDING_MISMATCH = "USEROP_BINDING_MISMATCH";
|
|
1040
1162
|
var HavenUserOpBindingRefusedError = class extends edge.HavenSigningError {
|
|
1041
1163
|
next_action = edge.AgentPaymentNextAction.StopAndTellUser;
|
|
@@ -1163,7 +1285,13 @@ var toolSchemas = {
|
|
|
1163
1285
|
// Mutually exclusive with payment_id / payload_hash (checked in the
|
|
1164
1286
|
// handler, since Zod's object-level refine on a raw shape used by
|
|
1165
1287
|
// several call sites is not the shape this schema is composed as).
|
|
1166
|
-
task_budget_id: v3.z.string().min(1).optional()
|
|
1288
|
+
task_budget_id: v3.z.string().min(1).optional(),
|
|
1289
|
+
// #3330: sign a sub-budget's own child delegation or its early-close
|
|
1290
|
+
// UserOp — same fetch-verify-sign channel as task_budget_id, against
|
|
1291
|
+
// `GET /sub-budgets/:id/sign-context`. Mutually exclusive with
|
|
1292
|
+
// payment_id / payload_hash AND with task_budget_id (checked in the
|
|
1293
|
+
// handler).
|
|
1294
|
+
sub_budget_id: v3.z.string().min(1).optional()
|
|
1167
1295
|
},
|
|
1168
1296
|
haven_x402_sign_header: {
|
|
1169
1297
|
// The parsed HTTP 402 PaymentRequired from the merchant. Typed as an object
|
|
@@ -1444,9 +1572,76 @@ function createToolHandlers(signer, options = {}) {
|
|
|
1444
1572
|
next_arguments: { task_budget_id: ctx.taskBudgetId, signature }
|
|
1445
1573
|
};
|
|
1446
1574
|
}
|
|
1575
|
+
async function signSubBudget(subBudgetId) {
|
|
1576
|
+
const identity = await options.signContext?.loadIdentity() ?? null;
|
|
1577
|
+
if (!identity) {
|
|
1578
|
+
throw new edge.HavenSigningError(
|
|
1579
|
+
`sub_budget_id signing needs the agent identity (identity.json next to the signer credentials), which this signer could not load. Re-run \`${edge.connectorRerunCommand()}\` to restore it.`
|
|
1580
|
+
);
|
|
1581
|
+
}
|
|
1582
|
+
const ctx = await fetchSubBudgetSignContext(
|
|
1583
|
+
identity,
|
|
1584
|
+
subBudgetId,
|
|
1585
|
+
options.signContext?.fetchImpl,
|
|
1586
|
+
void 0,
|
|
1587
|
+
options.signContext?.clientIdentity
|
|
1588
|
+
);
|
|
1589
|
+
let signature;
|
|
1590
|
+
if (ctx.purpose === "open") {
|
|
1591
|
+
try {
|
|
1592
|
+
edge.assertOwnSubBudgetChild(
|
|
1593
|
+
ctx.typedData,
|
|
1594
|
+
{
|
|
1595
|
+
chainId: ctx.expected.chainId,
|
|
1596
|
+
tokenAddress: ctx.expected.tokenAddress,
|
|
1597
|
+
periodAmountAtomic: ctx.expected.periodAmountAtomic,
|
|
1598
|
+
periodDurationSeconds: ctx.expected.periodDurationSeconds,
|
|
1599
|
+
startDate: ctx.expected.startDate,
|
|
1600
|
+
recipientAddress: ctx.expected.recipientAddress,
|
|
1601
|
+
expiresAt: ctx.expected.expiresAt,
|
|
1602
|
+
parentDelegationHash: ctx.expected.parentDelegationHash,
|
|
1603
|
+
delegateAccount: ctx.expected.delegateAccount,
|
|
1604
|
+
childDelegateAccount: ctx.expected.childDelegateAccount
|
|
1605
|
+
},
|
|
1606
|
+
signer.delegateAddress
|
|
1607
|
+
);
|
|
1608
|
+
} catch (err) {
|
|
1609
|
+
if (err instanceof edge.HavenTypedDataRefusedError) throw err;
|
|
1610
|
+
throw new edge.HavenTypedDataRefusedError(err instanceof Error ? err.message : String(err));
|
|
1611
|
+
}
|
|
1612
|
+
signature = await signer.signDelegationTypedData(ctx.typedData);
|
|
1613
|
+
} else {
|
|
1614
|
+
try {
|
|
1615
|
+
edge.assertUserOpTypedDataBinding(ctx.typedData, ctx.userOpHash);
|
|
1616
|
+
} catch (err) {
|
|
1617
|
+
if (err instanceof edge.HavenUserOpBindingError) throw new HavenUserOpBindingRefusedError(err.message);
|
|
1618
|
+
throw err;
|
|
1619
|
+
}
|
|
1620
|
+
try {
|
|
1621
|
+
edge.assertOwnSubBudgetCloseUserOp(
|
|
1622
|
+
ctx.typedData,
|
|
1623
|
+
{ delegationHash: ctx.expected.delegationHash },
|
|
1624
|
+
signer.delegateAddress
|
|
1625
|
+
);
|
|
1626
|
+
} catch (err) {
|
|
1627
|
+
if (err instanceof edge.HavenTypedDataRefusedError) throw err;
|
|
1628
|
+
throw new edge.HavenTypedDataRefusedError(err instanceof Error ? err.message : String(err));
|
|
1629
|
+
}
|
|
1630
|
+
signature = await signer.signDelegationTypedData(ctx.typedData);
|
|
1631
|
+
}
|
|
1632
|
+
await auditSigning("haven_sign", viem.hashTypedData(ctx.typedData));
|
|
1633
|
+
return {
|
|
1634
|
+
signature,
|
|
1635
|
+
sub_budget_id: ctx.subBudgetId,
|
|
1636
|
+
purpose: ctx.purpose,
|
|
1637
|
+
next_tool_name: "haven_submit",
|
|
1638
|
+
next_tool_server_role: "hosted",
|
|
1639
|
+
next_arguments: { sub_budget_id: ctx.subBudgetId, signature }
|
|
1640
|
+
};
|
|
1641
|
+
}
|
|
1447
1642
|
return {
|
|
1448
1643
|
haven_sign: async (input) => runTool(async () => {
|
|
1449
|
-
const args =
|
|
1644
|
+
const args = parseStrictFor("haven_sign", coerceX402Expected(input));
|
|
1450
1645
|
if (args.task_budget_id) {
|
|
1451
1646
|
if (args.payment_id || args.payload_hash) {
|
|
1452
1647
|
throw new v3.z.ZodError([
|
|
@@ -1457,8 +1652,29 @@ function createToolHandlers(signer, options = {}) {
|
|
|
1457
1652
|
}
|
|
1458
1653
|
]);
|
|
1459
1654
|
}
|
|
1655
|
+
if (args.sub_budget_id) {
|
|
1656
|
+
throw new v3.z.ZodError([
|
|
1657
|
+
{
|
|
1658
|
+
code: v3.z.ZodIssueCode.custom,
|
|
1659
|
+
path: ["sub_budget_id"],
|
|
1660
|
+
message: "task_budget_id is mutually exclusive with sub_budget_id \u2014 pass exactly one."
|
|
1661
|
+
}
|
|
1662
|
+
]);
|
|
1663
|
+
}
|
|
1460
1664
|
return signTaskBudget(args.task_budget_id);
|
|
1461
1665
|
}
|
|
1666
|
+
if (args.sub_budget_id) {
|
|
1667
|
+
if (args.payment_id || args.payload_hash) {
|
|
1668
|
+
throw new v3.z.ZodError([
|
|
1669
|
+
{
|
|
1670
|
+
code: v3.z.ZodIssueCode.custom,
|
|
1671
|
+
path: ["sub_budget_id"],
|
|
1672
|
+
message: "sub_budget_id is mutually exclusive with payment_id / payload_hash \u2014 pass exactly one."
|
|
1673
|
+
}
|
|
1674
|
+
]);
|
|
1675
|
+
}
|
|
1676
|
+
return signSubBudget(args.sub_budget_id);
|
|
1677
|
+
}
|
|
1462
1678
|
const resolved = await resolveSignContext(args, { allowDirectFallback: true });
|
|
1463
1679
|
const typedData = resolved?.ctx.typedData ?? resolveTypedData(args);
|
|
1464
1680
|
const payloadHash = resolved?.ctx.payloadHash ?? args.payload_hash;
|
|
@@ -1501,7 +1717,7 @@ function createToolHandlers(signer, options = {}) {
|
|
|
1501
1717
|
return { signature: result.signature, x402_binding: result.x402Binding, ...clientUpdateField(resolved) };
|
|
1502
1718
|
}),
|
|
1503
1719
|
haven_x402_sign_header: async (input) => runTool(async () => {
|
|
1504
|
-
const args =
|
|
1720
|
+
const args = parseStrictFor("haven_x402_sign_header", coercePaymentRequired(input));
|
|
1505
1721
|
const result = await signer.buildX402PaymentHeader(
|
|
1506
1722
|
args.payment_required,
|
|
1507
1723
|
args.x402_binding
|
|
@@ -1513,7 +1729,7 @@ function createToolHandlers(signer, options = {}) {
|
|
|
1513
1729
|
return { payment_header: result.paymentHeader, accepted: result.accepted };
|
|
1514
1730
|
}),
|
|
1515
1731
|
haven_sign_x402: async (input) => runTool(async () => {
|
|
1516
|
-
const args =
|
|
1732
|
+
const args = parseStrictFor("haven_sign_x402", coerceX402Expected(coercePaymentRequired(input)));
|
|
1517
1733
|
const resolved = await resolveSignContext(args, { allowDirectFallback: false });
|
|
1518
1734
|
const fetched = resolved?.kind === "x402" ? resolved.ctx : void 0;
|
|
1519
1735
|
const payloadHash = fetched?.payloadHash ?? args.payload_hash;
|
|
@@ -1549,7 +1765,7 @@ function createToolHandlers(signer, options = {}) {
|
|
|
1549
1765
|
};
|
|
1550
1766
|
}),
|
|
1551
1767
|
haven_sign_sweep_delegate: async (input) => runTool(async () => {
|
|
1552
|
-
const args =
|
|
1768
|
+
const args = parseStrictFor("haven_sign_sweep_delegate", input);
|
|
1553
1769
|
const result = await signer.signSweepAuthorization({
|
|
1554
1770
|
authorization: args.authorization,
|
|
1555
1771
|
expectedAuth: args.expected_auth,
|
|
@@ -1582,8 +1798,18 @@ function createToolHandlers(signer, options = {}) {
|
|
|
1582
1798
|
}
|
|
1583
1799
|
}
|
|
1584
1800
|
}
|
|
1585
|
-
function
|
|
1586
|
-
|
|
1801
|
+
function parseStrictFor(name, input) {
|
|
1802
|
+
const result = v3.z.object(toolSchemas[name]).strict().safeParse(input ?? {});
|
|
1803
|
+
if (result.success) return result.data;
|
|
1804
|
+
const topLevelUnknown = result.error.issues.flatMap(
|
|
1805
|
+
(issue) => issue.code === "unrecognized_keys" && issue.path.length === 0 ? issue.keys : []
|
|
1806
|
+
);
|
|
1807
|
+
if (topLevelUnknown.length === 0) throw result.error;
|
|
1808
|
+
const unknown = [...new Set(topLevelUnknown)];
|
|
1809
|
+
const err = new Error(
|
|
1810
|
+
`${UNSUPPORTED_ARGUMENT_MARKER} ${name} does not accept ${unknown.map((k) => `"${k}"`).join(", ")} \u2014 this signer predates that argument form. Nothing was signed or fetched.`
|
|
1811
|
+
);
|
|
1812
|
+
throw err;
|
|
1587
1813
|
}
|
|
1588
1814
|
function coerceX402Expected(input) {
|
|
1589
1815
|
if (!input || typeof input !== "object") return input;
|
|
@@ -1622,6 +1848,25 @@ async function runTool(fn) {
|
|
|
1622
1848
|
}
|
|
1623
1849
|
}
|
|
1624
1850
|
function normalizeError(err) {
|
|
1851
|
+
if (err instanceof Error && err.message.startsWith(UNSUPPORTED_ARGUMENT_MARKER)) {
|
|
1852
|
+
const unknown = [...err.message.matchAll(/"([^"]+)"/g)].map((m) => m[1]);
|
|
1853
|
+
return {
|
|
1854
|
+
success: false,
|
|
1855
|
+
code: "UNSUPPORTED_ARGUMENT",
|
|
1856
|
+
message: err.message.replace(`${UNSUPPORTED_ARGUMENT_MARKER} `, ""),
|
|
1857
|
+
unknown_arguments: unknown,
|
|
1858
|
+
signer_version: SIGNER_VERSION,
|
|
1859
|
+
fallback: edge.connectorUpgradeCommand(),
|
|
1860
|
+
next_action: edge.AgentPaymentNextAction.StopAndTellUser,
|
|
1861
|
+
...nextStepWireFields(
|
|
1862
|
+
signerRefusalStep({
|
|
1863
|
+
nextAction: edge.AgentPaymentNextAction.StopAndTellUser,
|
|
1864
|
+
nextTool: null,
|
|
1865
|
+
nextToolOmittedReason: `update @haven_ai/signer: run ${edge.connectorUpgradeCommand()} and the repair line it prints, then repeat the same call`
|
|
1866
|
+
})
|
|
1867
|
+
)
|
|
1868
|
+
};
|
|
1869
|
+
}
|
|
1625
1870
|
if (err instanceof v3.z.ZodError) {
|
|
1626
1871
|
return {
|
|
1627
1872
|
success: false,
|
|
@@ -1643,7 +1888,9 @@ function normalizeError(err) {
|
|
|
1643
1888
|
...nextStepWireFields(signerRefusalStep({
|
|
1644
1889
|
nextAction: edge.AgentPaymentNextAction.StopAndTellUser,
|
|
1645
1890
|
nextTool: null,
|
|
1646
|
-
|
|
1891
|
+
// #3412: the upgrade command, not "re-run the connector" — a bare re-run
|
|
1892
|
+
// stops at "Missing --setup" on an existing install.
|
|
1893
|
+
nextToolOmittedReason: `update @haven_ai/signer: run ${edge.connectorUpgradeCommand()} and the repair line it prints, then repeat the same call`
|
|
1647
1894
|
}))
|
|
1648
1895
|
};
|
|
1649
1896
|
}
|
|
@@ -1858,8 +2105,6 @@ async function writeAckFile(path$1, hash) {
|
|
|
1858
2105
|
"utf8"
|
|
1859
2106
|
);
|
|
1860
2107
|
}
|
|
1861
|
-
var SIGNER_NAME = "@haven_ai/signer";
|
|
1862
|
-
var SIGNER_VERSION = "0.6.0-alpha.0";
|
|
1863
2108
|
async function resolveEdgeSigner(options = {}) {
|
|
1864
2109
|
const { signer } = await resolveSignerRuntime(options);
|
|
1865
2110
|
return signer;
|
|
@@ -1914,12 +2159,14 @@ function buildSignerMcpServer(signer, options = {}) {
|
|
|
1914
2159
|
clientIdentity: edge.havenClientIdentity(SIGNER_NAME, SIGNER_VERSION)
|
|
1915
2160
|
}
|
|
1916
2161
|
});
|
|
1917
|
-
const registerTool = server.
|
|
2162
|
+
const registerTool = server.registerTool.bind(server);
|
|
1918
2163
|
for (const name of Object.keys(toolSchemas)) {
|
|
1919
2164
|
registerTool(
|
|
1920
2165
|
name,
|
|
1921
|
-
|
|
1922
|
-
|
|
2166
|
+
{
|
|
2167
|
+
description: toolDescriptions[name],
|
|
2168
|
+
inputSchema: zod.z.object(toolSchemas[name]).passthrough()
|
|
2169
|
+
},
|
|
1923
2170
|
async (args) => toMcpResult(await handlers[name](args))
|
|
1924
2171
|
);
|
|
1925
2172
|
}
|
|
@@ -1986,6 +2233,7 @@ exports.SIGNER_NAME = SIGNER_NAME;
|
|
|
1986
2233
|
exports.SIGNER_VERSION = SIGNER_VERSION;
|
|
1987
2234
|
exports.SUPPORTED_SWEEP_BINDING_VERSIONS = SUPPORTED_SWEEP_BINDING_VERSIONS;
|
|
1988
2235
|
exports.SUPPORTED_X402_EXPECTED_VERSIONS = SUPPORTED_X402_EXPECTED_VERSIONS;
|
|
2236
|
+
exports.UNSUPPORTED_ARGUMENT_MARKER = UNSUPPORTED_ARGUMENT_MARKER;
|
|
1989
2237
|
exports.appendSigningAuditEntry = appendSigningAuditEntry;
|
|
1990
2238
|
exports.assertSupportedBindingVersion = assertSupportedBindingVersion;
|
|
1991
2239
|
exports.assertSupportedNodeVersion = assertSupportedNodeVersion;
|