@haven_ai/signer 0.2.0-alpha.0 → 0.3.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 +45 -3
- package/dist/cli.cjs +107 -19
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +107 -19
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +107 -19
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +29 -23
- package/dist/index.d.ts +29 -23
- package/dist/index.js +107 -19
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.d.cts
CHANGED
|
@@ -282,18 +282,8 @@ declare function signerInstructions(): string;
|
|
|
282
282
|
interface SignerCredentials {
|
|
283
283
|
delegateKey: string;
|
|
284
284
|
agentId?: string;
|
|
285
|
-
/**
|
|
286
|
-
* The Haven account (smart account) the agent spends from — #2908, the
|
|
287
|
-
* account-vocabulary name. Same value as `safeAddress`.
|
|
288
|
-
*/
|
|
285
|
+
/** The Haven account (smart account) the agent spends from. */
|
|
289
286
|
accountAddress?: string;
|
|
290
|
-
/**
|
|
291
|
-
* @deprecated #2908 — same value as {@link SignerCredentials.accountAddress}.
|
|
292
|
-
* Removed from this shape in the release after the one carrying #2908
|
|
293
|
-
* (#2914). The credential-FILE keys it was read from are a different
|
|
294
|
-
* matter — see `readAccountAddressField`.
|
|
295
|
-
*/
|
|
296
|
-
safeAddress?: string;
|
|
297
287
|
chainId?: number;
|
|
298
288
|
network?: string;
|
|
299
289
|
x402BindingSigner?: string;
|
|
@@ -305,11 +295,11 @@ interface RawCredentialFile {
|
|
|
305
295
|
delegateKey?: unknown;
|
|
306
296
|
agent_id?: unknown;
|
|
307
297
|
agentId?: unknown;
|
|
308
|
-
/**
|
|
298
|
+
/** What `@haven_ai/connect` writes. */
|
|
309
299
|
account_address?: unknown;
|
|
310
|
-
/** Pre-#2908 spelling — read PERMANENTLY, see `readAccountAddressField`. */
|
|
300
|
+
/** Pre-#2908 spelling — read PERMANENTLY (a credential file never rewrites itself), see `readAccountAddressField`. */
|
|
311
301
|
safe_address?: unknown;
|
|
312
|
-
/** Pre-#2908 spelling — read PERMANENTLY, see `readAccountAddressField`. */
|
|
302
|
+
/** Pre-#2908 spelling — read PERMANENTLY (a credential file never rewrites itself), see `readAccountAddressField`. */
|
|
313
303
|
safeAddress?: unknown;
|
|
314
304
|
chain_id?: unknown;
|
|
315
305
|
chainId?: unknown;
|
|
@@ -346,11 +336,11 @@ declare function loadSignerCredentials(path?: string | undefined): Promise<Signe
|
|
|
346
336
|
*/
|
|
347
337
|
declare function readAccountAddressField(raw: Pick<RawCredentialFile, 'account_address' | 'safe_address' | 'safeAddress'>): string | undefined;
|
|
348
338
|
/**
|
|
349
|
-
* The account address off the process environment
|
|
350
|
-
*
|
|
351
|
-
*
|
|
352
|
-
*
|
|
353
|
-
*
|
|
339
|
+
* The account address off the process environment: `HAVEN_ACCOUNT_ADDRESS`
|
|
340
|
+
* (the survivor, decided on #2906). The two names the dashboard handoff
|
|
341
|
+
* emitted before #2908 — `HAVEN_WALLET_ADDRESS` and `HAVEN_SAFE_ADDRESS` —
|
|
342
|
+
* were window-scoped (unlike the credential-FILE fallbacks) and are retired
|
|
343
|
+
* as of #2914.
|
|
354
344
|
*/
|
|
355
345
|
declare function readAccountAddressEnv(env: NodeJS.ProcessEnv): string | undefined;
|
|
356
346
|
/**
|
|
@@ -368,12 +358,20 @@ interface SigningAuditEntry {
|
|
|
368
358
|
tool: SignerToolName;
|
|
369
359
|
payload_hash: string;
|
|
370
360
|
delegate_address: string;
|
|
361
|
+
/**
|
|
362
|
+
* Serialized key kept as `safe_address` deliberately (#2914 review): this
|
|
363
|
+
* is a persisted JSONL field, and an audit log written by an earlier
|
|
364
|
+
* release never rewrites itself — renaming the key here would fork the
|
|
365
|
+
* format mid-file. `SigningAuditContext.accountAddress` (the in-memory
|
|
366
|
+
* field this is built from) carries the account-vocabulary name; only the
|
|
367
|
+
* on-disk spelling stays put.
|
|
368
|
+
*/
|
|
371
369
|
safe_address?: string;
|
|
372
370
|
chain_id?: number;
|
|
373
371
|
}
|
|
374
372
|
interface SigningAuditContext {
|
|
375
373
|
delegateAddress: string;
|
|
376
|
-
|
|
374
|
+
accountAddress?: string;
|
|
377
375
|
chainId?: number;
|
|
378
376
|
auditPath?: string;
|
|
379
377
|
}
|
|
@@ -420,9 +418,17 @@ interface ToolFailure {
|
|
|
420
418
|
/**
|
|
421
419
|
* #1309: precise recovery guidance as DATA, not just prose inside `message`
|
|
422
420
|
* — the same text the hosted quote's advisory `signer_compatibility.fallback`
|
|
423
|
-
* carries when the refusal is an out-of-date signer.
|
|
421
|
+
* carries when the refusal is an out-of-date signer. #3001 reuses this same
|
|
422
|
+
* field for every sign-context refusal (`'typed_data_b64'`).
|
|
424
423
|
*/
|
|
425
424
|
fallback?: string;
|
|
425
|
+
/**
|
|
426
|
+
* #3001: present on `SIGN_CONTEXT_REFUSED` — the HTTP status the backend
|
|
427
|
+
* answered the `/x402/:id/sign-context` fetch with (404, 410, …).
|
|
428
|
+
*/
|
|
429
|
+
http_status?: number;
|
|
430
|
+
/** #3001: the backend's own `error_code` on `SIGN_CONTEXT_REFUSED` (`expired`, `already_executed`, `not_signable`, `sign_context_unavailable`). */
|
|
431
|
+
backend_error_code?: string;
|
|
426
432
|
}
|
|
427
433
|
type ToolPayload<T = unknown> = ToolSuccess<T> | ToolFailure;
|
|
428
434
|
interface ToolHandlerOptions {
|
|
@@ -443,7 +449,7 @@ declare function createToolHandlers(signer: EdgeSigner, options?: ToolHandlerOpt
|
|
|
443
449
|
|
|
444
450
|
interface SignerConsentInput {
|
|
445
451
|
delegateAddress: string;
|
|
446
|
-
|
|
452
|
+
accountAddress?: string;
|
|
447
453
|
agentId?: string;
|
|
448
454
|
chainId?: number;
|
|
449
455
|
network?: string;
|
|
@@ -468,7 +474,7 @@ declare function renderSignerConsentBlock(input: SignerConsentInput, hash: strin
|
|
|
468
474
|
declare function ensureSignerConsent(input: SignerConsentInput, options?: SignerConsentOptions): Promise<SignerConsentDecision>;
|
|
469
475
|
|
|
470
476
|
declare const SIGNER_NAME = "@haven_ai/signer";
|
|
471
|
-
declare const SIGNER_VERSION = "0.
|
|
477
|
+
declare const SIGNER_VERSION = "0.3.0-alpha.0";
|
|
472
478
|
interface SignerOptions {
|
|
473
479
|
/** Path to a Haven credential JSON file (delegate_key is read from it). */
|
|
474
480
|
credentialsPath?: string;
|
package/dist/index.d.ts
CHANGED
|
@@ -282,18 +282,8 @@ declare function signerInstructions(): string;
|
|
|
282
282
|
interface SignerCredentials {
|
|
283
283
|
delegateKey: string;
|
|
284
284
|
agentId?: string;
|
|
285
|
-
/**
|
|
286
|
-
* The Haven account (smart account) the agent spends from — #2908, the
|
|
287
|
-
* account-vocabulary name. Same value as `safeAddress`.
|
|
288
|
-
*/
|
|
285
|
+
/** The Haven account (smart account) the agent spends from. */
|
|
289
286
|
accountAddress?: string;
|
|
290
|
-
/**
|
|
291
|
-
* @deprecated #2908 — same value as {@link SignerCredentials.accountAddress}.
|
|
292
|
-
* Removed from this shape in the release after the one carrying #2908
|
|
293
|
-
* (#2914). The credential-FILE keys it was read from are a different
|
|
294
|
-
* matter — see `readAccountAddressField`.
|
|
295
|
-
*/
|
|
296
|
-
safeAddress?: string;
|
|
297
287
|
chainId?: number;
|
|
298
288
|
network?: string;
|
|
299
289
|
x402BindingSigner?: string;
|
|
@@ -305,11 +295,11 @@ interface RawCredentialFile {
|
|
|
305
295
|
delegateKey?: unknown;
|
|
306
296
|
agent_id?: unknown;
|
|
307
297
|
agentId?: unknown;
|
|
308
|
-
/**
|
|
298
|
+
/** What `@haven_ai/connect` writes. */
|
|
309
299
|
account_address?: unknown;
|
|
310
|
-
/** Pre-#2908 spelling — read PERMANENTLY, see `readAccountAddressField`. */
|
|
300
|
+
/** Pre-#2908 spelling — read PERMANENTLY (a credential file never rewrites itself), see `readAccountAddressField`. */
|
|
311
301
|
safe_address?: unknown;
|
|
312
|
-
/** Pre-#2908 spelling — read PERMANENTLY, see `readAccountAddressField`. */
|
|
302
|
+
/** Pre-#2908 spelling — read PERMANENTLY (a credential file never rewrites itself), see `readAccountAddressField`. */
|
|
313
303
|
safeAddress?: unknown;
|
|
314
304
|
chain_id?: unknown;
|
|
315
305
|
chainId?: unknown;
|
|
@@ -346,11 +336,11 @@ declare function loadSignerCredentials(path?: string | undefined): Promise<Signe
|
|
|
346
336
|
*/
|
|
347
337
|
declare function readAccountAddressField(raw: Pick<RawCredentialFile, 'account_address' | 'safe_address' | 'safeAddress'>): string | undefined;
|
|
348
338
|
/**
|
|
349
|
-
* The account address off the process environment
|
|
350
|
-
*
|
|
351
|
-
*
|
|
352
|
-
*
|
|
353
|
-
*
|
|
339
|
+
* The account address off the process environment: `HAVEN_ACCOUNT_ADDRESS`
|
|
340
|
+
* (the survivor, decided on #2906). The two names the dashboard handoff
|
|
341
|
+
* emitted before #2908 — `HAVEN_WALLET_ADDRESS` and `HAVEN_SAFE_ADDRESS` —
|
|
342
|
+
* were window-scoped (unlike the credential-FILE fallbacks) and are retired
|
|
343
|
+
* as of #2914.
|
|
354
344
|
*/
|
|
355
345
|
declare function readAccountAddressEnv(env: NodeJS.ProcessEnv): string | undefined;
|
|
356
346
|
/**
|
|
@@ -368,12 +358,20 @@ interface SigningAuditEntry {
|
|
|
368
358
|
tool: SignerToolName;
|
|
369
359
|
payload_hash: string;
|
|
370
360
|
delegate_address: string;
|
|
361
|
+
/**
|
|
362
|
+
* Serialized key kept as `safe_address` deliberately (#2914 review): this
|
|
363
|
+
* is a persisted JSONL field, and an audit log written by an earlier
|
|
364
|
+
* release never rewrites itself — renaming the key here would fork the
|
|
365
|
+
* format mid-file. `SigningAuditContext.accountAddress` (the in-memory
|
|
366
|
+
* field this is built from) carries the account-vocabulary name; only the
|
|
367
|
+
* on-disk spelling stays put.
|
|
368
|
+
*/
|
|
371
369
|
safe_address?: string;
|
|
372
370
|
chain_id?: number;
|
|
373
371
|
}
|
|
374
372
|
interface SigningAuditContext {
|
|
375
373
|
delegateAddress: string;
|
|
376
|
-
|
|
374
|
+
accountAddress?: string;
|
|
377
375
|
chainId?: number;
|
|
378
376
|
auditPath?: string;
|
|
379
377
|
}
|
|
@@ -420,9 +418,17 @@ interface ToolFailure {
|
|
|
420
418
|
/**
|
|
421
419
|
* #1309: precise recovery guidance as DATA, not just prose inside `message`
|
|
422
420
|
* — the same text the hosted quote's advisory `signer_compatibility.fallback`
|
|
423
|
-
* carries when the refusal is an out-of-date signer.
|
|
421
|
+
* carries when the refusal is an out-of-date signer. #3001 reuses this same
|
|
422
|
+
* field for every sign-context refusal (`'typed_data_b64'`).
|
|
424
423
|
*/
|
|
425
424
|
fallback?: string;
|
|
425
|
+
/**
|
|
426
|
+
* #3001: present on `SIGN_CONTEXT_REFUSED` — the HTTP status the backend
|
|
427
|
+
* answered the `/x402/:id/sign-context` fetch with (404, 410, …).
|
|
428
|
+
*/
|
|
429
|
+
http_status?: number;
|
|
430
|
+
/** #3001: the backend's own `error_code` on `SIGN_CONTEXT_REFUSED` (`expired`, `already_executed`, `not_signable`, `sign_context_unavailable`). */
|
|
431
|
+
backend_error_code?: string;
|
|
426
432
|
}
|
|
427
433
|
type ToolPayload<T = unknown> = ToolSuccess<T> | ToolFailure;
|
|
428
434
|
interface ToolHandlerOptions {
|
|
@@ -443,7 +449,7 @@ declare function createToolHandlers(signer: EdgeSigner, options?: ToolHandlerOpt
|
|
|
443
449
|
|
|
444
450
|
interface SignerConsentInput {
|
|
445
451
|
delegateAddress: string;
|
|
446
|
-
|
|
452
|
+
accountAddress?: string;
|
|
447
453
|
agentId?: string;
|
|
448
454
|
chainId?: number;
|
|
449
455
|
network?: string;
|
|
@@ -468,7 +474,7 @@ declare function renderSignerConsentBlock(input: SignerConsentInput, hash: strin
|
|
|
468
474
|
declare function ensureSignerConsent(input: SignerConsentInput, options?: SignerConsentOptions): Promise<SignerConsentDecision>;
|
|
469
475
|
|
|
470
476
|
declare const SIGNER_NAME = "@haven_ai/signer";
|
|
471
|
-
declare const SIGNER_VERSION = "0.
|
|
477
|
+
declare const SIGNER_VERSION = "0.3.0-alpha.0";
|
|
472
478
|
interface SignerOptions {
|
|
473
479
|
/** Path to a Haven credential JSON file (delegate_key is read from it). */
|
|
474
480
|
credentialsPath?: string;
|
package/dist/index.js
CHANGED
|
@@ -506,7 +506,6 @@ async function loadSignerCredentials(path = process.env.HAVEN_CREDENTIALS) {
|
|
|
506
506
|
delegateKey: envKey,
|
|
507
507
|
agentId: stringField(process.env.HAVEN_AGENT_ID),
|
|
508
508
|
accountAddress,
|
|
509
|
-
safeAddress: accountAddress,
|
|
510
509
|
chainId: chainIdField(process.env.HAVEN_CHAIN_ID, "HAVEN_CHAIN_ID"),
|
|
511
510
|
network: stringField(process.env.HAVEN_NETWORK),
|
|
512
511
|
x402BindingSigner: stringField(process.env.HAVEN_X402_BINDING_SIGNER)
|
|
@@ -541,7 +540,6 @@ async function loadFromFile(path) {
|
|
|
541
540
|
delegateKey,
|
|
542
541
|
agentId: stringField(raw.agent_id ?? raw.agentId),
|
|
543
542
|
accountAddress,
|
|
544
|
-
safeAddress: accountAddress,
|
|
545
543
|
chainId: chainIdField(raw.chain_id ?? raw.chainId, "chain_id"),
|
|
546
544
|
network: stringField(raw.network),
|
|
547
545
|
x402BindingSigner: stringField(
|
|
@@ -554,7 +552,22 @@ function readAccountAddressField(raw) {
|
|
|
554
552
|
return stringField(raw.account_address ?? raw.safe_address ?? raw.safeAddress);
|
|
555
553
|
}
|
|
556
554
|
function readAccountAddressEnv(env) {
|
|
557
|
-
|
|
555
|
+
const current = stringField(env.HAVEN_ACCOUNT_ADDRESS);
|
|
556
|
+
for (const name of ["HAVEN_WALLET_ADDRESS", "HAVEN_SAFE_ADDRESS"]) {
|
|
557
|
+
const retired = stringField(env[name]);
|
|
558
|
+
if (!retired) continue;
|
|
559
|
+
if (!current) {
|
|
560
|
+
throw new Error(
|
|
561
|
+
`${name} is retired (#2906) \u2014 Haven accounts are addressed as accounts, not Safes. Set HAVEN_ACCOUNT_ADDRESS to the same value. Refusing rather than ignoring it, because an unset account address silently skips the sweep-destination check.`
|
|
562
|
+
);
|
|
563
|
+
}
|
|
564
|
+
if (retired.toLowerCase() !== current.toLowerCase()) {
|
|
565
|
+
throw new Error(
|
|
566
|
+
`${name} and HAVEN_ACCOUNT_ADDRESS were both set to different addresses. Remove the retired name, or make them match \u2014 picking one silently would hide the mismatch.`
|
|
567
|
+
);
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
return current;
|
|
558
571
|
}
|
|
559
572
|
function stringField(value) {
|
|
560
573
|
return typeof value === "string" && value.trim() ? value.trim() : void 0;
|
|
@@ -611,7 +624,7 @@ function createSigningAuditEntry(tool, payloadHash, context, now = /* @__PURE__
|
|
|
611
624
|
payload_hash: payloadHash,
|
|
612
625
|
delegate_address: context.delegateAddress
|
|
613
626
|
};
|
|
614
|
-
if (context.
|
|
627
|
+
if (context.accountAddress) entry.safe_address = context.accountAddress;
|
|
615
628
|
if (typeof context.chainId === "number") entry.chain_id = context.chainId;
|
|
616
629
|
return entry;
|
|
617
630
|
}
|
|
@@ -627,6 +640,49 @@ function stableStringify(value) {
|
|
|
627
640
|
const object = value;
|
|
628
641
|
return `{${Object.keys(object).sort().map((key) => `${JSON.stringify(key)}:${stableStringify(object[key])}`).join(",")}}`;
|
|
629
642
|
}
|
|
643
|
+
var HavenSignContextError = class extends HavenSigningError {
|
|
644
|
+
/**
|
|
645
|
+
* #3001 / #3010 review: the recovery path, when signing OTHER bytes is
|
|
646
|
+
* actually a remedy — a transport failure (timeout, unreachable) or a
|
|
647
|
+
* body this signer could not read. `typed_data_b64` is NOT in the default
|
|
648
|
+
* quote result since #1272: obtain it by re-running the SAME quote tool
|
|
649
|
+
* with the SAME idempotency_key plus `include_signing_payload: true`.
|
|
650
|
+
* Absent on a backend REFUSAL: an expired, executed or unsignable intent
|
|
651
|
+
* cannot be rescued by re-signing its bytes.
|
|
652
|
+
*/
|
|
653
|
+
fallback;
|
|
654
|
+
/**
|
|
655
|
+
* #3001: `AgentPaymentNextAction` values the signer already emits — the
|
|
656
|
+
* version-mismatch refusal's `stop_and_tell_user` for the classes where
|
|
657
|
+
* retrying the same call cannot help, and `payment_window_expired` (with
|
|
658
|
+
* `retry_with_new_quote`) for the backend's 410 `expired`, exactly as the
|
|
659
|
+
* plain `HavenError` branch already does for that code.
|
|
660
|
+
*/
|
|
661
|
+
next_action;
|
|
662
|
+
retry_with_new_quote;
|
|
663
|
+
/** Present only for `SIGN_CONTEXT_REFUSED` (the backend's HTTP status). */
|
|
664
|
+
http_status;
|
|
665
|
+
/** Present only for `SIGN_CONTEXT_REFUSED`: the backend's own `error_code`. */
|
|
666
|
+
backend_error_code;
|
|
667
|
+
constructor(message, code, refusal) {
|
|
668
|
+
super(message);
|
|
669
|
+
this.code = code;
|
|
670
|
+
this.name = "HavenSignContextError";
|
|
671
|
+
if (code === "SIGN_CONTEXT_REFUSED") {
|
|
672
|
+
this.http_status = refusal?.httpStatus;
|
|
673
|
+
this.backend_error_code = refusal?.errorCode;
|
|
674
|
+
if (refusal?.httpStatus === 410 || refusal?.errorCode === "expired") {
|
|
675
|
+
this.next_action = AgentPaymentNextAction.PaymentWindowExpired;
|
|
676
|
+
this.retry_with_new_quote = true;
|
|
677
|
+
} else {
|
|
678
|
+
this.next_action = AgentPaymentNextAction.StopAndTellUser;
|
|
679
|
+
}
|
|
680
|
+
} else {
|
|
681
|
+
this.fallback = "typed_data_b64";
|
|
682
|
+
this.next_action = AgentPaymentNextAction.StopAndTellUser;
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
};
|
|
630
686
|
async function loadHavenIdentity(credentialsPath) {
|
|
631
687
|
if (!credentialsPath) return null;
|
|
632
688
|
try {
|
|
@@ -641,30 +697,50 @@ async function loadHavenIdentity(credentialsPath) {
|
|
|
641
697
|
return null;
|
|
642
698
|
}
|
|
643
699
|
}
|
|
644
|
-
|
|
700
|
+
var SIGN_CONTEXT_TIMEOUT_MS = 15e3;
|
|
701
|
+
async function fetchX402SignContext(identity, paymentId, fetchImpl = fetch, timeoutMs = SIGN_CONTEXT_TIMEOUT_MS) {
|
|
645
702
|
let response;
|
|
646
703
|
try {
|
|
647
704
|
response = await fetchImpl(
|
|
648
705
|
`${identity.apiUrl}/x402/${encodeURIComponent(paymentId)}/sign-context`,
|
|
649
|
-
{
|
|
706
|
+
{
|
|
707
|
+
headers: { Authorization: `Bearer ${identity.apiKey}` },
|
|
708
|
+
signal: AbortSignal.timeout(timeoutMs)
|
|
709
|
+
}
|
|
650
710
|
);
|
|
651
711
|
} catch (err) {
|
|
652
|
-
|
|
653
|
-
|
|
712
|
+
const timedOut = err instanceof Error && (err.name === "TimeoutError" || err.name === "AbortError");
|
|
713
|
+
throw new HavenSignContextError(
|
|
714
|
+
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"
|
|
654
716
|
);
|
|
655
717
|
}
|
|
656
|
-
|
|
718
|
+
let body;
|
|
719
|
+
try {
|
|
720
|
+
body = await response.json();
|
|
721
|
+
} catch (err) {
|
|
722
|
+
if (err instanceof Error && (err.name === "TimeoutError" || err.name === "AbortError")) {
|
|
723
|
+
throw new HavenSignContextError(
|
|
724
|
+
`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"
|
|
726
|
+
);
|
|
727
|
+
}
|
|
728
|
+
body = {};
|
|
729
|
+
}
|
|
657
730
|
if (!response.ok) {
|
|
658
731
|
const detail = typeof body.error === "string" ? body.error : `HTTP ${response.status}`;
|
|
659
|
-
throw new
|
|
660
|
-
`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." : "")
|
|
732
|
+
throw new HavenSignContextError(
|
|
733
|
+
`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
|
+
"SIGN_CONTEXT_REFUSED",
|
|
735
|
+
{ httpStatus: response.status, errorCode: typeof body.error_code === "string" ? body.error_code : void 0 }
|
|
661
736
|
);
|
|
662
737
|
}
|
|
663
738
|
const signData = body.sign_data;
|
|
664
739
|
const x402Expected = body.x402_expected;
|
|
665
740
|
if (!signData || typeof signData.hash !== "string" || !signData.typed_data || typeof signData.typed_data !== "object" || !x402Expected) {
|
|
666
|
-
throw new
|
|
667
|
-
"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."
|
|
741
|
+
throw new HavenSignContextError(
|
|
742
|
+
"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"
|
|
668
744
|
);
|
|
669
745
|
}
|
|
670
746
|
const paymentRequired = body.payment_required;
|
|
@@ -1015,7 +1091,7 @@ function createToolHandlers(signer, options = {}) {
|
|
|
1015
1091
|
authorization: args.authorization,
|
|
1016
1092
|
expectedAuth: args.expected_auth,
|
|
1017
1093
|
// Cross-check `to` against the Safe in the local credential when present.
|
|
1018
|
-
expectedSafe: options.audit?.
|
|
1094
|
+
expectedSafe: options.audit?.accountAddress
|
|
1019
1095
|
});
|
|
1020
1096
|
await auditSigning(
|
|
1021
1097
|
"haven_sign_sweep_delegate",
|
|
@@ -1095,6 +1171,18 @@ function normalizeError(err) {
|
|
|
1095
1171
|
next_action: AgentPaymentNextAction.StopAndTellUser
|
|
1096
1172
|
};
|
|
1097
1173
|
}
|
|
1174
|
+
if (err instanceof HavenSignContextError) {
|
|
1175
|
+
return {
|
|
1176
|
+
success: false,
|
|
1177
|
+
code: err.code,
|
|
1178
|
+
message: err.message,
|
|
1179
|
+
next_action: err.next_action,
|
|
1180
|
+
...err.fallback !== void 0 ? { fallback: err.fallback } : {},
|
|
1181
|
+
...err.retry_with_new_quote ? { retry_with_new_quote: true } : {},
|
|
1182
|
+
...err.http_status !== void 0 ? { http_status: err.http_status } : {},
|
|
1183
|
+
...err.backend_error_code !== void 0 ? { backend_error_code: err.backend_error_code } : {}
|
|
1184
|
+
};
|
|
1185
|
+
}
|
|
1098
1186
|
if (err instanceof HavenSigningError) {
|
|
1099
1187
|
return { success: false, code: err.code, message: err.message };
|
|
1100
1188
|
}
|
|
@@ -1128,7 +1216,7 @@ var SIGNER_CONSENT_SURFACE_VERSION = 2;
|
|
|
1128
1216
|
function computeSignerConsentHash(input) {
|
|
1129
1217
|
const identity = [
|
|
1130
1218
|
input.delegateAddress.toLowerCase(),
|
|
1131
|
-
(input.
|
|
1219
|
+
(input.accountAddress ?? "").toLowerCase(),
|
|
1132
1220
|
input.agentId ?? "",
|
|
1133
1221
|
input.chainId ?? "",
|
|
1134
1222
|
input.network ?? ""
|
|
@@ -1147,7 +1235,7 @@ function renderSignerConsentBlock(input, hash) {
|
|
|
1147
1235
|
"",
|
|
1148
1236
|
`Delegate address: ${input.delegateAddress}`
|
|
1149
1237
|
];
|
|
1150
|
-
lines.push(`Haven wallet: ${input.
|
|
1238
|
+
lines.push(`Haven wallet: ${input.accountAddress ?? "not provided to this signer"}`);
|
|
1151
1239
|
if (input.agentId) lines.push(`Agent ID: ${input.agentId}`);
|
|
1152
1240
|
if (typeof input.chainId === "number") lines.push(`Chain ID: ${input.chainId}`);
|
|
1153
1241
|
if (input.network) lines.push(`Network: ${input.network}`);
|
|
@@ -1241,7 +1329,7 @@ async function writeAckFile(path, hash) {
|
|
|
1241
1329
|
);
|
|
1242
1330
|
}
|
|
1243
1331
|
var SIGNER_NAME = "@haven_ai/signer";
|
|
1244
|
-
var SIGNER_VERSION = "0.
|
|
1332
|
+
var SIGNER_VERSION = "0.3.0-alpha.0";
|
|
1245
1333
|
async function resolveEdgeSigner(options = {}) {
|
|
1246
1334
|
const { signer } = await resolveSignerRuntime(options);
|
|
1247
1335
|
return signer;
|
|
@@ -1280,7 +1368,7 @@ function buildSignerMcpServer(signer, options = {}) {
|
|
|
1280
1368
|
audit: {
|
|
1281
1369
|
auditPath: options.auditPath ?? defaultSigningAuditPath(credentialsPath),
|
|
1282
1370
|
delegateAddress: signer.delegateAddress,
|
|
1283
|
-
|
|
1371
|
+
accountAddress: options.credentials?.accountAddress,
|
|
1284
1372
|
chainId: options.credentials?.chainId
|
|
1285
1373
|
},
|
|
1286
1374
|
// #1263: the payment_id signing path — the ONLY network call this server
|
|
@@ -1330,7 +1418,7 @@ async function runSignerConsentGate(signer, credentials, options) {
|
|
|
1330
1418
|
return ensureSignerConsent(
|
|
1331
1419
|
{
|
|
1332
1420
|
delegateAddress: signer.delegateAddress,
|
|
1333
|
-
|
|
1421
|
+
accountAddress: credentials?.accountAddress,
|
|
1334
1422
|
agentId: credentials?.agentId,
|
|
1335
1423
|
chainId: credentials?.chainId,
|
|
1336
1424
|
network: credentials?.network,
|