kawasekit 0.3.0 → 0.5.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/dist/index.d.cts CHANGED
@@ -8,7 +8,7 @@ export { C as ChainNotSupportedError, g as getChain, i as isSupportedChainId, s
8
8
  export { C as ConfiguredKernelClient, I as IssueSessionKeyParams, K as KAWASEKIT_SESSION_ENVELOPE_VERSION, a as KawasekitSessionEnvelope, b as KawasekitSessionEnvelopeVersion, c as KawasekitSessionPolicySummary, R as RestoreSessionAccountParams, d as RevokeSessionKeyParams, e as RevokeSessionKeyResult, f as RotateSessionKeyParams, g as RotateSessionKeyResult, S as SessionEnvelopeChainMismatchError, h as SessionEnvelopeParseError, i as SessionEnvelopeSignerMismatchError, j as SessionEnvelopeVersionError, T as TransferJpycInputError, k as TransferJpycParams, l as TransferJpycResult, m as issueSessionKey, p as parseSessionEnvelope, r as restoreSessionAccount, n as revokeSessionKey, o as rotateSessionKey, s as serializeSessionEnvelope, t as transferJpyc } from './index-DFChv_fT.cjs';
9
9
  export { CreateJpycDailyLimitPoliciesParams, ONE_DAY_SECONDS, createJpycDailyLimitPolicies } from './policy/index.cjs';
10
10
  export { C as CreateSpendingPolicyParams, P as PolicyDecision, S as SpendState, a as SpendingPolicy, b as SpendingPolicyConfigError, T as TokenLimit, c as createSpendingPolicy, e as evaluateSpendingPolicy, m as mergeSpendState } from './spending-policy-CGIaBpg-.cjs';
11
- export { CoSignConnection, CoSignFrame, CoSignRequestAuthenticator, CoSignTransport, CoSignUnavailableError, CreateLocalPolicyGatedSignerParams, Mpc2pCoSignAgent, Mpc2pSignerParams, Mpc2pStepOutcome, PolicyGatedSignerConfigError, WIRE_VERSION, WireIntent, assertNonBypassable, canonicalIntentBytes, createLocalPolicyGatedSigner, createMpc2pPolicyGatedSigner, requireNonBypassable, toWireIntent } from './signer/index.cjs';
11
+ export { CoSignConnection, CoSignFrame, CoSignRequestAuthenticator, CoSignRequestEnvelope, CoSignTransport, CoSignUnavailableError, CreateLocalPolicyGatedSignerParams, Mpc2pCoSignAgent, Mpc2pSignerParams, Mpc2pStepOutcome, PolicyGatedSignerConfigError, WIRE_VERSION, WireIntent, assertNonBypassable, canonicalRequestBytes, createLocalPolicyGatedSigner, createMpc2pPolicyGatedSigner, requireNonBypassable, toWireIntent } from './signer/index.cjs';
12
12
  export { E as EnforcementLevel, N as NonBypassableEnforcement, P as PaymentIntent, a as PolicyGatedSigner, b as PolicyRejection, S as SignResult, c as SignerDescription } from './types-BLJU67HZ.cjs';
13
13
  export { C as CancelAuthorizationMessage, E as Eip3009Domain, K as KnownAssetDomain, a as KnownAssetId, R as ReceiveWithAuthorizationMessage, b as ResolvedAsset, S as SignedAuthorization, T as TransferWithAuthorizationMessage, X as X402AssetParam, c as X402TokenDomain, d as authorizationDeadlineFromNow, e as cancelAuthorizationTypes, f as deriveAuthorizationNonce, g as generateAuthorizationNonce, h as getKnownAssetDomain, l as listKnownAssetIds, r as receiveWithAuthorizationTypes, i as resolvedAssetToEip3009Domain, s as signCancelAuthorization, j as signReceiveWithAuthorization, k as signTransferWithAuthorization, t as transferWithAuthorizationTypes } from './asset-domain-4Ioxqn28.cjs';
14
14
  export { B as BuildPaymentRequiredResponseParams, a as BuildPaymentRequirementsParams, C as CreateCoinbaseFacilitatorParams, b as CreateHttpFacilitatorParams, c as CreateSelfFacilitatorParams, d as CreateX402PaymentSignerParams, K as KawaseChain, S as SignX402PaymentParams, W as WrapFetchParams, X as X402Fetch, e as X402InvalidConfigError, f as X402InvalidPayloadError, g as X402PaymentSigner, h as X402PolicyRejectedError, i as X402_DEFAULT_AUTHORIZATION_LIFETIME_SECONDS, j as X402_DEFAULT_MAX_TIMEOUT_SECONDS, k as X402_FACILITATOR_ERROR_CODES, l as X402_HEADER_IDEMPOTENCY_KEY, m as X402_HEADER_PAYMENT_REQUIRED, n as X402_HEADER_PAYMENT_RESPONSE, o as X402_HEADER_PAYMENT_SIGNATURE, p as buildPaymentRequiredResponse, q as buildPaymentRequirements, r as createCoinbaseFacilitator, s as createHttpFacilitator, t as createSelfFacilitator, u as createX402PaymentSigner, v as decodePaymentRequiredHeader, w as decodePaymentResponseHeader, x as decodePaymentSignatureHeader, y as deriveReceiptTimeoutMs, z as encodePaymentRequiredHeader, A as encodePaymentResponseHeader, D as encodePaymentSignatureHeader, E as wrapFetch, F as zerodevRpcUrl } from './index-CAmTA8xR.cjs';
package/dist/index.d.ts CHANGED
@@ -8,7 +8,7 @@ export { C as ChainNotSupportedError, g as getChain, i as isSupportedChainId, s
8
8
  export { C as ConfiguredKernelClient, I as IssueSessionKeyParams, K as KAWASEKIT_SESSION_ENVELOPE_VERSION, a as KawasekitSessionEnvelope, b as KawasekitSessionEnvelopeVersion, c as KawasekitSessionPolicySummary, R as RestoreSessionAccountParams, d as RevokeSessionKeyParams, e as RevokeSessionKeyResult, f as RotateSessionKeyParams, g as RotateSessionKeyResult, S as SessionEnvelopeChainMismatchError, h as SessionEnvelopeParseError, i as SessionEnvelopeSignerMismatchError, j as SessionEnvelopeVersionError, T as TransferJpycInputError, k as TransferJpycParams, l as TransferJpycResult, m as issueSessionKey, p as parseSessionEnvelope, r as restoreSessionAccount, n as revokeSessionKey, o as rotateSessionKey, s as serializeSessionEnvelope, t as transferJpyc } from './index-CSpNGigO.js';
9
9
  export { CreateJpycDailyLimitPoliciesParams, ONE_DAY_SECONDS, createJpycDailyLimitPolicies } from './policy/index.js';
10
10
  export { C as CreateSpendingPolicyParams, P as PolicyDecision, S as SpendState, a as SpendingPolicy, b as SpendingPolicyConfigError, T as TokenLimit, c as createSpendingPolicy, e as evaluateSpendingPolicy, m as mergeSpendState } from './spending-policy-BD2Mpm-L.js';
11
- export { CoSignConnection, CoSignFrame, CoSignRequestAuthenticator, CoSignTransport, CoSignUnavailableError, CreateLocalPolicyGatedSignerParams, Mpc2pCoSignAgent, Mpc2pSignerParams, Mpc2pStepOutcome, PolicyGatedSignerConfigError, WIRE_VERSION, WireIntent, assertNonBypassable, canonicalIntentBytes, createLocalPolicyGatedSigner, createMpc2pPolicyGatedSigner, requireNonBypassable, toWireIntent } from './signer/index.js';
11
+ export { CoSignConnection, CoSignFrame, CoSignRequestAuthenticator, CoSignRequestEnvelope, CoSignTransport, CoSignUnavailableError, CreateLocalPolicyGatedSignerParams, Mpc2pCoSignAgent, Mpc2pSignerParams, Mpc2pStepOutcome, PolicyGatedSignerConfigError, WIRE_VERSION, WireIntent, assertNonBypassable, canonicalRequestBytes, createLocalPolicyGatedSigner, createMpc2pPolicyGatedSigner, requireNonBypassable, toWireIntent } from './signer/index.js';
12
12
  export { E as EnforcementLevel, N as NonBypassableEnforcement, P as PaymentIntent, a as PolicyGatedSigner, b as PolicyRejection, S as SignResult, c as SignerDescription } from './types-BLJU67HZ.js';
13
13
  export { C as CancelAuthorizationMessage, E as Eip3009Domain, K as KnownAssetDomain, a as KnownAssetId, R as ReceiveWithAuthorizationMessage, b as ResolvedAsset, S as SignedAuthorization, T as TransferWithAuthorizationMessage, X as X402AssetParam, c as X402TokenDomain, d as authorizationDeadlineFromNow, e as cancelAuthorizationTypes, f as deriveAuthorizationNonce, g as generateAuthorizationNonce, h as getKnownAssetDomain, l as listKnownAssetIds, r as receiveWithAuthorizationTypes, i as resolvedAssetToEip3009Domain, s as signCancelAuthorization, j as signReceiveWithAuthorization, k as signTransferWithAuthorization, t as transferWithAuthorizationTypes } from './asset-domain-4Ioxqn28.js';
14
14
  export { B as BuildPaymentRequiredResponseParams, a as BuildPaymentRequirementsParams, C as CreateCoinbaseFacilitatorParams, b as CreateHttpFacilitatorParams, c as CreateSelfFacilitatorParams, d as CreateX402PaymentSignerParams, K as KawaseChain, S as SignX402PaymentParams, W as WrapFetchParams, X as X402Fetch, e as X402InvalidConfigError, f as X402InvalidPayloadError, g as X402PaymentSigner, h as X402PolicyRejectedError, i as X402_DEFAULT_AUTHORIZATION_LIFETIME_SECONDS, j as X402_DEFAULT_MAX_TIMEOUT_SECONDS, k as X402_FACILITATOR_ERROR_CODES, l as X402_HEADER_IDEMPOTENCY_KEY, m as X402_HEADER_PAYMENT_REQUIRED, n as X402_HEADER_PAYMENT_RESPONSE, o as X402_HEADER_PAYMENT_SIGNATURE, p as buildPaymentRequiredResponse, q as buildPaymentRequirements, r as createCoinbaseFacilitator, s as createHttpFacilitator, t as createSelfFacilitator, u as createX402PaymentSigner, v as decodePaymentRequiredHeader, w as decodePaymentResponseHeader, x as decodePaymentSignatureHeader, y as deriveReceiptTimeoutMs, z as encodePaymentRequiredHeader, A as encodePaymentResponseHeader, D as encodePaymentSignatureHeader, E as wrapFetch, F as zerodevRpcUrl } from './index-C9tbaHPF.js';
package/dist/index.js CHANGED
@@ -1,12 +1,12 @@
1
1
  export { TransferJpycInputError, transferJpyc } from './chunk-RJLDKDWL.js';
2
- export { X402_DEFAULT_AUTHORIZATION_LIFETIME_SECONDS, X402_FACILITATOR_ERROR_CODES, createCoinbaseFacilitator, createHttpFacilitator, createSelfFacilitator, createX402PaymentSigner, deriveReceiptTimeoutMs, wrapFetch } from './chunk-G3UC6SME.js';
2
+ export { X402_DEFAULT_AUTHORIZATION_LIFETIME_SECONDS, X402_FACILITATOR_ERROR_CODES, createCoinbaseFacilitator, createHttpFacilitator, createSelfFacilitator, createX402PaymentSigner, deriveReceiptTimeoutMs, wrapFetch } from './chunk-P5563RGP.js';
3
3
  export { X402_DEFAULT_MAX_TIMEOUT_SECONDS, X402_HEADER_IDEMPOTENCY_KEY, X402_HEADER_PAYMENT_REQUIRED, X402_HEADER_PAYMENT_RESPONSE, X402_HEADER_PAYMENT_SIGNATURE, buildPaymentRequiredResponse, buildPaymentRequirements, createX402Handler, decodePaymentRequiredHeader, decodePaymentResponseHeader, decodePaymentSignatureHeader, encodePaymentRequiredHeader, encodePaymentResponseHeader, encodePaymentSignatureHeader } from './chunk-PVUKX6IF.js';
4
4
  export { extractAcceptedNetworks, invokeHookSafely } from './chunk-LEHWRDVS.js';
5
5
  export { createIdempotencyKeyBuilder, createInMemoryIdempotencyStore, deriveIdempotencyKey, normalizeIntentText } from './chunk-TTX3RBIZ.js';
6
6
  export { IdempotencyConfigError, IdempotencyRecordParseError, IdempotencyRecordVersionError, KAWASEKIT_IDEMPOTENCY_RECORD_VERSION, X402_VERSION, chainIdToX402Network, isX402Network, parseIdempotencyRecord, serializeIdempotencyRecord, x402NetworkToChainId } from './chunk-QHUCU5YX.js';
7
7
  export { KAWASEKIT_SESSION_ENVELOPE_VERSION, SessionEnvelopeChainMismatchError, SessionEnvelopeParseError, SessionEnvelopeSignerMismatchError, SessionEnvelopeVersionError, createAgentSmartAccount, issueSessionKey, parseSessionEnvelope, restoreSessionAccount, revokeSessionKey, rotateSessionKey, serializeSessionEnvelope } from './chunk-N3CVLISJ.js';
8
- export { WIRE_VERSION, canonicalIntentBytes, createLocalPolicyGatedSigner, createMpc2pPolicyGatedSigner, toWireIntent } from './chunk-EUW7AZ4P.js';
9
- export { CoSignUnavailableError, PolicyGatedSignerConfigError, assertNonBypassable, authorizationDeadlineFromNow, cancelAuthorizationTypes, deriveAuthorizationNonce, generateAuthorizationNonce, getKnownAssetDomain, listKnownAssetIds, receiveWithAuthorizationTypes, requireNonBypassable, resolvedAssetToEip3009Domain, signCancelAuthorization, signReceiveWithAuthorization, signTransferWithAuthorization, transferWithAuthorizationTypes } from './chunk-5TTOAVHE.js';
8
+ export { WIRE_VERSION, canonicalRequestBytes, createLocalPolicyGatedSigner, createMpc2pPolicyGatedSigner, toWireIntent } from './chunk-S2ZSX2VS.js';
9
+ export { CoSignUnavailableError, PolicyGatedSignerConfigError, assertNonBypassable, authorizationDeadlineFromNow, cancelAuthorizationTypes, deriveAuthorizationNonce, generateAuthorizationNonce, getKnownAssetDomain, listKnownAssetIds, receiveWithAuthorizationTypes, requireNonBypassable, resolvedAssetToEip3009Domain, signCancelAuthorization, signReceiveWithAuthorization, signTransferWithAuthorization, transferWithAuthorizationTypes } from './chunk-VJUXTVSW.js';
10
10
  export { X402InvalidConfigError, X402InvalidPayloadError, X402PolicyRejectedError } from './chunk-WMVJNPX2.js';
11
11
  export { ONE_DAY_SECONDS, createJpycDailyLimitPolicies } from './chunk-E47SIVFY.js';
12
12
  export { SpendingPolicyConfigError, createSpendingPolicy, evaluateSpendingPolicy, mergeSpendState } from './chunk-6CNAYQOL.js';
@@ -17,9 +17,17 @@ var PolicyGatedSignerConfigError = class extends Error {
17
17
  }
18
18
  };
19
19
  var CoSignUnavailableError = class extends Error {
20
+ /**
21
+ * `true` when the failure is the **transient transport class** (connect failed /
22
+ * connection dropped) — the only class the adapter's bounded retry replays
23
+ * (RFC m6-3a §4.7: never a delivered rejection, never a ban/identifiable-abort,
24
+ * never a protocol anomaly or timeout). Defaults to `false`.
25
+ */
26
+ transient;
20
27
  constructor(message, options) {
21
- super(message, options);
28
+ super(message, options?.cause === void 0 ? void 0 : { cause: options.cause });
22
29
  this.name = "CoSignUnavailableError";
30
+ this.transient = options?.transient ?? false;
23
31
  }
24
32
  };
25
33
 
@@ -362,7 +370,8 @@ function createLocalPolicyGatedSigner(params) {
362
370
  }
363
371
 
364
372
  // src/signer/mpc-2p-wire.ts
365
- var WIRE_VERSION = 1;
373
+ var WIRE_VERSION = 2;
374
+ var MAX_FRAME_BYTES = 8 * 1024 * 1024;
366
375
  function toWireIntent(intent) {
367
376
  return {
368
377
  token: intent.token.toLowerCase(),
@@ -375,17 +384,23 @@ function toWireIntent(intent) {
375
384
  nonce: intent.nonce.toLowerCase()
376
385
  };
377
386
  }
378
- function canonicalIntentBytes(intent) {
387
+ function canonicalRequestBytes(env) {
388
+ const i = env.intent;
379
389
  const lines = [
380
- "kawasekit-mpc-2p/cosign-request/v2",
381
- `token=${intent.token.toLowerCase()}`,
382
- `chainId=${intent.chainId}`,
383
- `from=${intent.from.toLowerCase()}`,
384
- `to=${intent.to.toLowerCase()}`,
385
- `value=${intent.value}`,
386
- `validAfter=${intent.validAfter}`,
387
- `validBefore=${intent.validBefore}`,
388
- `nonce=${intent.nonce.toLowerCase()}`
390
+ "kawasekit-mpc-2p/cosign-request/v3",
391
+ "wireVersion=2",
392
+ `ceremonyId=${env.ceremonyId}`,
393
+ `ssid=${env.ssid}`,
394
+ `token=${i.token.toLowerCase()}`,
395
+ `chainId=${i.chainId}`,
396
+ `from=${i.from.toLowerCase()}`,
397
+ `to=${i.to.toLowerCase()}`,
398
+ `value=${i.value}`,
399
+ `validAfter=${i.validAfter}`,
400
+ `validBefore=${i.validBefore}`,
401
+ `nonce=${i.nonce.toLowerCase()}`,
402
+ `freshnessTs=${env.freshnessTs}`,
403
+ `freshnessNonce=${env.freshnessNonce.toLowerCase()}`
389
404
  ];
390
405
  return new TextEncoder().encode(lines.map((l) => `${l}
391
406
  `).join(""));
@@ -411,6 +426,12 @@ function createMpc2pPolicyGatedSigner(params) {
411
426
  const { agent, transport, authenticator, session } = params;
412
427
  const pinned = resolveAssetParam(params.asset);
413
428
  const from = viem.getAddress(params.from);
429
+ const wire = params.wire ?? {};
430
+ const maxAttempts = Math.max(1, wire.maxAttempts ?? 2);
431
+ const ceremonyTimeoutMs = wire.ceremonyTimeoutMs ?? 3e4;
432
+ const minWindowSecs = wire.minWindowSecs ?? 30;
433
+ const clockSkewBudgetSecs = wire.clockSkewBudgetSecs ?? 30;
434
+ const maxFrameBytes = wire.maxFrameBytes ?? MAX_FRAME_BYTES;
414
435
  const agentEoa = viem.getAddress(agent.groupEoa());
415
436
  if (agentEoa !== from) {
416
437
  throw new PolicyGatedSignerConfigError(
@@ -447,16 +468,59 @@ function createMpc2pPolicyGatedSigner(params) {
447
468
  nonce: intent.nonce
448
469
  }
449
470
  });
450
- const authTag = await authenticator.tag(canonicalIntentBytes(intent));
451
- const conn = await openConnection(transport);
452
- try {
453
- return await runCeremony(conn, agent, session.id, intent, digest, authTag);
454
- } finally {
471
+ const validBeforeSecs = Number(intent.validBefore);
472
+ const nowSecs = Math.floor(Date.now() / 1e3);
473
+ if (validBeforeSecs - nowSecs < minWindowSecs + clockSkewBudgetSecs) {
474
+ throw new CoSignUnavailableError(
475
+ `intent.validBefore (${validBeforeSecs}) leaves under the minimum ceremony window (${minWindowSecs}s + ${clockSkewBudgetSecs}s clock-skew budget) \u2014 refusing a co-sign that risks being born expired (W11)`
476
+ );
477
+ }
478
+ const deadlineMs = Math.min(
479
+ Date.now() + ceremonyTimeoutMs,
480
+ (validBeforeSecs - clockSkewBudgetSecs) * 1e3
481
+ );
482
+ let lastTransient;
483
+ for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
484
+ if (Date.now() >= deadlineMs) break;
485
+ const env = {
486
+ ceremonyId: globalThis.crypto.randomUUID(),
487
+ ssid: globalThis.crypto.randomUUID(),
488
+ intent,
489
+ freshnessTs: Math.floor(Date.now() / 1e3),
490
+ freshnessNonce: viem.toHex(globalThis.crypto.getRandomValues(new Uint8Array(16)))
491
+ };
492
+ const authTag = await authenticator.tag(canonicalRequestBytes(env));
455
493
  try {
456
- await conn.close();
457
- } catch {
494
+ const conn = await openConnection(transport);
495
+ try {
496
+ return await runCeremony({
497
+ conn,
498
+ agent,
499
+ sessionId: session.id,
500
+ env,
501
+ digest,
502
+ authTag,
503
+ from,
504
+ deadlineMs,
505
+ maxFrameBytes
506
+ });
507
+ } finally {
508
+ try {
509
+ await conn.close();
510
+ } catch {
511
+ }
512
+ }
513
+ } catch (error) {
514
+ if (error instanceof CoSignUnavailableError && error.transient && attempt < maxAttempts) {
515
+ lastTransient = error;
516
+ continue;
517
+ }
518
+ throw error;
458
519
  }
459
520
  }
521
+ throw lastTransient ?? new CoSignUnavailableError(
522
+ "co-sign ceremony budget exhausted before validBefore (W11) \u2014 no attempt could start"
523
+ );
460
524
  },
461
525
  describe() {
462
526
  return {
@@ -475,23 +539,32 @@ async function openConnection(transport) {
475
539
  try {
476
540
  return await transport.connect();
477
541
  } catch (cause) {
478
- throw new CoSignUnavailableError("co-signer connection failed", { cause });
542
+ throw new CoSignUnavailableError("co-signer connection failed", { cause, transient: true });
479
543
  }
480
544
  }
481
- async function runCeremony(conn, agent, sessionId, intent, digest, authTag) {
545
+ async function runCeremony(ctx) {
546
+ const { conn, agent, env, digest, from } = ctx;
482
547
  const send = async (frame) => {
483
548
  try {
484
- await conn.send(frame);
549
+ await withDeadline(conn.send(frame), ctx.deadlineMs);
485
550
  } catch (cause) {
486
- throw new CoSignUnavailableError("co-sign connection dropped while sending", { cause });
551
+ if (cause instanceof CoSignUnavailableError) throw cause;
552
+ throw new CoSignUnavailableError("co-sign connection dropped while sending", {
553
+ cause,
554
+ transient: true
555
+ });
487
556
  }
488
557
  };
489
558
  const recv = async () => {
490
559
  let frame;
491
560
  try {
492
- frame = await conn.recv();
561
+ frame = await withDeadline(conn.recv(), ctx.deadlineMs);
493
562
  } catch (cause) {
494
- throw new CoSignUnavailableError("co-sign connection dropped mid-ceremony", { cause });
563
+ if (cause instanceof CoSignUnavailableError) throw cause;
564
+ throw new CoSignUnavailableError("co-sign connection dropped mid-ceremony", {
565
+ cause,
566
+ transient: true
567
+ });
495
568
  }
496
569
  if (frame.wire_version !== WIRE_VERSION) {
497
570
  throw new CoSignUnavailableError(
@@ -503,9 +576,13 @@ async function runCeremony(conn, agent, sessionId, intent, digest, authTag) {
503
576
  await send({
504
577
  wire_version: WIRE_VERSION,
505
578
  kind: "request",
506
- session_id: sessionId,
507
- intent: toWireIntent(intent),
508
- auth_tag: authTag
579
+ session_id: ctx.sessionId,
580
+ ceremony_id: env.ceremonyId,
581
+ ssid: env.ssid,
582
+ intent: toWireIntent(env.intent),
583
+ freshness_ts: env.freshnessTs,
584
+ freshness_nonce: env.freshnessNonce,
585
+ auth_tag: ctx.authTag
509
586
  });
510
587
  let first;
511
588
  try {
@@ -519,6 +596,12 @@ async function runCeremony(conn, agent, sessionId, intent, digest, authTag) {
519
596
  const frame = await recv();
520
597
  switch (frame.kind) {
521
598
  case "round": {
599
+ const payloadBytes = (frame.payload.length - 2) / 2;
600
+ if (payloadBytes > ctx.maxFrameBytes) {
601
+ throw new CoSignUnavailableError(
602
+ `co-signer sent a ${payloadBytes}-byte round frame (bound ${ctx.maxFrameBytes}) \u2014 refused pre-decode`
603
+ );
604
+ }
522
605
  let step;
523
606
  try {
524
607
  step = agent.step(frame.payload);
@@ -533,15 +616,16 @@ async function runCeremony(conn, agent, sessionId, intent, digest, authTag) {
533
616
  break;
534
617
  }
535
618
  case "result": {
619
+ const signature = assembleSignature({ r: frame.r, s: frame.s, v: frame.v });
536
620
  if (agentSig === null) {
537
- throw new CoSignUnavailableError(
538
- "the backend returned a result before the agent derived a signature"
539
- );
621
+ await verifyRecovers(digest, signature, frame.s, from, "idempotent-replay");
622
+ return { ok: true, signature, intent: env.intent };
540
623
  }
541
624
  if (frame.r.toLowerCase() !== agentSig.r.toLowerCase() || frame.s.toLowerCase() !== agentSig.s.toLowerCase() || frame.v !== agentSig.v) {
542
625
  throw new CoSignUnavailableError("the backend and agent derived different signatures");
543
626
  }
544
- return { ok: true, signature: assembleSignature(agentSig), intent };
627
+ await verifyRecovers(digest, signature, frame.s, from, "co-signed");
628
+ return { ok: true, signature, intent: env.intent };
545
629
  }
546
630
  case "rejection": {
547
631
  if (POLICY_REASONS.has(frame.reason)) {
@@ -560,6 +644,52 @@ async function runCeremony(conn, agent, sessionId, intent, digest, authTag) {
560
644
  }
561
645
  }
562
646
  }
647
+ function withDeadline(promise, deadlineMs) {
648
+ const remaining = deadlineMs - Date.now();
649
+ if (remaining <= 0) {
650
+ return Promise.reject(
651
+ new CoSignUnavailableError(
652
+ "co-sign ceremony timed out (the deadline fires before validBefore \u2014 W11)"
653
+ )
654
+ );
655
+ }
656
+ return new Promise((resolve, reject) => {
657
+ const timer = setTimeout(() => {
658
+ reject(
659
+ new CoSignUnavailableError(
660
+ "co-sign ceremony timed out (the deadline fires before validBefore \u2014 W11)"
661
+ )
662
+ );
663
+ }, remaining);
664
+ promise.then(
665
+ (value) => {
666
+ clearTimeout(timer);
667
+ resolve(value);
668
+ },
669
+ (cause) => {
670
+ clearTimeout(timer);
671
+ reject(cause);
672
+ }
673
+ );
674
+ });
675
+ }
676
+ var SECP256K1_HALF_N = 0x7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0n;
677
+ async function verifyRecovers(digest, signature, s, from, what) {
678
+ if (BigInt(s) > SECP256K1_HALF_N) {
679
+ throw new CoSignUnavailableError(`the ${what} signature is not low-S (EIP-2)`);
680
+ }
681
+ let recovered;
682
+ try {
683
+ recovered = await viem.recoverAddress({ hash: digest, signature });
684
+ } catch (cause) {
685
+ throw new CoSignUnavailableError(`the ${what} signature failed to recover`, { cause });
686
+ }
687
+ if (viem.getAddress(recovered) !== from) {
688
+ throw new CoSignUnavailableError(
689
+ `the ${what} signature recovers to ${viem.getAddress(recovered)}, not the group EOA ${from}`
690
+ );
691
+ }
692
+ }
563
693
  function assembleSignature(sig) {
564
694
  const yParity = sig.v - 27;
565
695
  if (yParity !== 0 && yParity !== 1) {
@@ -569,10 +699,11 @@ function assembleSignature(sig) {
569
699
  }
570
700
 
571
701
  exports.CoSignUnavailableError = CoSignUnavailableError;
702
+ exports.MAX_FRAME_BYTES = MAX_FRAME_BYTES;
572
703
  exports.PolicyGatedSignerConfigError = PolicyGatedSignerConfigError;
573
704
  exports.WIRE_VERSION = WIRE_VERSION;
574
705
  exports.assertNonBypassable = assertNonBypassable;
575
- exports.canonicalIntentBytes = canonicalIntentBytes;
706
+ exports.canonicalRequestBytes = canonicalRequestBytes;
576
707
  exports.createLocalPolicyGatedSigner = createLocalPolicyGatedSigner;
577
708
  exports.createMpc2pPolicyGatedSigner = createMpc2pPolicyGatedSigner;
578
709
  exports.requireNonBypassable = requireNonBypassable;