kawasekit 0.4.0 → 0.6.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.
Files changed (37) hide show
  1. package/dist/{chunk-6CNAYQOL.js → chunk-7YKRKV6D.js} +18 -4
  2. package/dist/chunk-7YKRKV6D.js.map +1 -0
  3. package/dist/{chunk-FD6Q4NB2.js → chunk-NY6VLXQB.js} +137 -32
  4. package/dist/chunk-NY6VLXQB.js.map +1 -0
  5. package/dist/{chunk-G3UC6SME.js → chunk-P5563RGP.js} +3 -3
  6. package/dist/{chunk-G3UC6SME.js.map → chunk-P5563RGP.js.map} +1 -1
  7. package/dist/{chunk-5TTOAVHE.js → chunk-VJUXTVSW.js} +11 -3
  8. package/dist/chunk-VJUXTVSW.js.map +1 -0
  9. package/dist/{chunk-E47SIVFY.js → chunk-X54U6GU4.js} +18 -5
  10. package/dist/chunk-X54U6GU4.js.map +1 -0
  11. package/dist/cli/index.cjs +29 -3
  12. package/dist/cli/index.cjs.map +1 -1
  13. package/dist/cli/index.js +6 -6
  14. package/dist/index.cjs +169 -30
  15. package/dist/index.cjs.map +1 -1
  16. package/dist/index.d.cts +1 -1
  17. package/dist/index.d.ts +1 -1
  18. package/dist/index.js +5 -5
  19. package/dist/policy/index.cjs +27 -3
  20. package/dist/policy/index.cjs.map +1 -1
  21. package/dist/policy/index.d.cts +41 -3
  22. package/dist/policy/index.d.ts +41 -3
  23. package/dist/policy/index.js +2 -2
  24. package/dist/signer/index.cjs +141 -27
  25. package/dist/signer/index.cjs.map +1 -1
  26. package/dist/signer/index.d.cts +58 -2
  27. package/dist/signer/index.d.ts +58 -2
  28. package/dist/signer/index.js +3 -3
  29. package/dist/{spending-policy-BD2Mpm-L.d.ts → spending-policy-BBgXDrVD.d.ts} +10 -3
  30. package/dist/{spending-policy-CGIaBpg-.d.cts → spending-policy-CwXcTFD_.d.cts} +10 -3
  31. package/dist/x402/index.cjs.map +1 -1
  32. package/dist/x402/index.js +2 -2
  33. package/package.json +1 -1
  34. package/dist/chunk-5TTOAVHE.js.map +0 -1
  35. package/dist/chunk-6CNAYQOL.js.map +0 -1
  36. package/dist/chunk-E47SIVFY.js.map +0 -1
  37. package/dist/chunk-FD6Q4NB2.js.map +0 -1
package/dist/cli/index.js CHANGED
@@ -1,16 +1,16 @@
1
1
  #!/usr/bin/env node
2
2
  import { transferJpyc } from '../chunk-RJLDKDWL.js';
3
- import '../chunk-G3UC6SME.js';
3
+ import '../chunk-P5563RGP.js';
4
4
  import '../chunk-PVUKX6IF.js';
5
5
  import '../chunk-LEHWRDVS.js';
6
6
  import '../chunk-TTX3RBIZ.js';
7
7
  import '../chunk-QHUCU5YX.js';
8
8
  import { issueSessionKey, serializeSessionEnvelope, restoreSessionAccount, revokeSessionKey, rotateSessionKey, parseSessionEnvelope } from '../chunk-N3CVLISJ.js';
9
- import '../chunk-FD6Q4NB2.js';
10
- import '../chunk-5TTOAVHE.js';
9
+ import '../chunk-NY6VLXQB.js';
10
+ import '../chunk-VJUXTVSW.js';
11
11
  import '../chunk-WMVJNPX2.js';
12
- import { createJpycDailyLimitPolicies } from '../chunk-E47SIVFY.js';
13
- import '../chunk-6CNAYQOL.js';
12
+ import { createJpycDailyLimitPolicies } from '../chunk-X54U6GU4.js';
13
+ import '../chunk-7YKRKV6D.js';
14
14
  import { JPYC_DECIMALS, getJpycAddress, jpycAbi } from '../chunk-KT7XDT2T.js';
15
15
  import { zerodevRpcUrl, polygon, polygonAmoy } from '../chunk-SOTYGX67.js';
16
16
  import { __commonJS, __require, __toESM } from '../chunk-7D4SUZUM.js';
@@ -980,7 +980,7 @@ function registerTransferCommand(program2) {
980
980
  }
981
981
 
982
982
  // cli/index.ts
983
- var CLI_VERSION = "0.4.0" ;
983
+ var CLI_VERSION = "0.6.0" ;
984
984
  var program = new Command();
985
985
  program.name("kawasekit").description(
986
986
  "kawasekit \u2014 CLI for the kawasekit SDK (AI-agent stablecoin payments, Japan-first, JPYC-native)."
package/dist/index.cjs CHANGED
@@ -363,6 +363,20 @@ async function transferJpyc(kernelClient, params) {
363
363
  success: receipt.success
364
364
  };
365
365
  }
366
+ function normalizeRecipientAllowlist(allowlist) {
367
+ const seen = /* @__PURE__ */ new Set();
368
+ const normalized = [];
369
+ for (const entry of allowlist) {
370
+ const checksummed = viem.getAddress(entry);
371
+ if (!seen.has(checksummed)) {
372
+ seen.add(checksummed);
373
+ normalized.push(checksummed);
374
+ }
375
+ }
376
+ return normalized;
377
+ }
378
+
379
+ // src/policy/daily-limit.ts
366
380
  var ONE_DAY_SECONDS = 86400;
367
381
  function createJpycDailyLimitPolicies(params) {
368
382
  if (params.maxPerTransfer <= 0n) {
@@ -375,6 +389,17 @@ function createJpycDailyLimitPolicies(params) {
375
389
  `createJpycDailyLimitPolicies: maxTransfersPerDay must be a positive integer, got ${params.maxTransfersPerDay}.`
376
390
  );
377
391
  }
392
+ const recipients = params.recipientAllowlist === void 0 || params.recipientAllowlist === "any" ? null : normalizeRecipientAllowlist(params.recipientAllowlist);
393
+ if (recipients !== null && recipients.length === 0) {
394
+ throw new Error(
395
+ 'createJpycDailyLimitPolicies: recipientAllowlist must not be empty \u2014 omit it or pass "any" to allow any recipient.'
396
+ );
397
+ }
398
+ if (recipients !== null && params.callPolicyVersion === policies.CallPolicyVersion.V0_0_1) {
399
+ throw new Error(
400
+ "createJpycDailyLimitPolicies: recipientAllowlist requires callPolicyVersion V0_0_2 or later (the ONE_OF condition is unsupported on V0_0_1)."
401
+ );
402
+ }
378
403
  const callPolicy = policies.toCallPolicy({
379
404
  policyVersion: params.callPolicyVersion ?? policies.CallPolicyVersion.V0_0_4,
380
405
  permissions: [
@@ -383,8 +408,9 @@ function createJpycDailyLimitPolicies(params) {
383
408
  abi: jpycAbi,
384
409
  functionName: "transfer",
385
410
  args: [
386
- // Recipient: any address allowed (no allowlist in M2).
387
- null,
411
+ // Recipient (`to`): unrestricted (`null`) unless an allowlist resolved
412
+ // above, in which case it is constrained via the ONE_OF condition.
413
+ recipients === null ? null : { condition: policies.ParamCondition.ONE_OF, value: recipients },
388
414
  // value: must be ≤ maxPerTransfer.
389
415
  {
390
416
  condition: policies.ParamCondition.LESS_THAN_OR_EQUAL,
@@ -501,7 +527,7 @@ function createSpendingPolicy(params) {
501
527
  }
502
528
  return { token, maxPerSign: l.maxPerSign };
503
529
  });
504
- const recipientAllowlist = params.recipientAllowlist === "any" ? "any" : params.recipientAllowlist.map((a) => viem.getAddress(a));
530
+ const recipientAllowlist = params.recipientAllowlist === "any" ? "any" : normalizeRecipientAllowlist(params.recipientAllowlist);
505
531
  return {
506
532
  version: "1",
507
533
  session: { id: params.session.id, notAfter: params.session.notAfter },
@@ -533,9 +559,17 @@ var PolicyGatedSignerConfigError = class extends Error {
533
559
  }
534
560
  };
535
561
  var CoSignUnavailableError = class extends Error {
562
+ /**
563
+ * `true` when the failure is the **transient transport class** (connect failed /
564
+ * connection dropped) — the only class the adapter's bounded retry replays
565
+ * (RFC m6-3a §4.7: never a delivered rejection, never a ban/identifiable-abort,
566
+ * never a protocol anomaly or timeout). Defaults to `false`.
567
+ */
568
+ transient;
536
569
  constructor(message, options) {
537
- super(message, options);
570
+ super(message, options?.cause === void 0 ? void 0 : { cause: options.cause });
538
571
  this.name = "CoSignUnavailableError";
572
+ this.transient = options?.transient ?? false;
539
573
  }
540
574
  };
541
575
 
@@ -837,6 +871,7 @@ function createLocalPolicyGatedSigner(params) {
837
871
 
838
872
  // src/signer/mpc-2p-wire.ts
839
873
  var WIRE_VERSION = 2;
874
+ var MAX_FRAME_BYTES = 8 * 1024 * 1024;
840
875
  function toWireIntent(intent) {
841
876
  return {
842
877
  token: intent.token.toLowerCase(),
@@ -891,6 +926,12 @@ function createMpc2pPolicyGatedSigner(params) {
891
926
  const { agent, transport, authenticator, session } = params;
892
927
  const pinned = resolveAssetParam(params.asset);
893
928
  const from = viem.getAddress(params.from);
929
+ const wire = params.wire ?? {};
930
+ const maxAttempts = Math.max(1, wire.maxAttempts ?? 2);
931
+ const ceremonyTimeoutMs = wire.ceremonyTimeoutMs ?? 3e4;
932
+ const minWindowSecs = wire.minWindowSecs ?? 30;
933
+ const clockSkewBudgetSecs = wire.clockSkewBudgetSecs ?? 30;
934
+ const maxFrameBytes = wire.maxFrameBytes ?? MAX_FRAME_BYTES;
894
935
  const agentEoa = viem.getAddress(agent.groupEoa());
895
936
  if (agentEoa !== from) {
896
937
  throw new PolicyGatedSignerConfigError(
@@ -927,23 +968,59 @@ function createMpc2pPolicyGatedSigner(params) {
927
968
  nonce: intent.nonce
928
969
  }
929
970
  });
930
- const env = {
931
- ceremonyId: globalThis.crypto.randomUUID(),
932
- ssid: globalThis.crypto.randomUUID(),
933
- intent,
934
- freshnessTs: Math.floor(Date.now() / 1e3),
935
- freshnessNonce: viem.toHex(globalThis.crypto.getRandomValues(new Uint8Array(16)))
936
- };
937
- const authTag = await authenticator.tag(canonicalRequestBytes(env));
938
- const conn = await openConnection(transport);
939
- try {
940
- return await runCeremony(conn, agent, session.id, env, digest, authTag);
941
- } finally {
971
+ const validBeforeSecs = Number(intent.validBefore);
972
+ const nowSecs = Math.floor(Date.now() / 1e3);
973
+ if (validBeforeSecs - nowSecs < minWindowSecs + clockSkewBudgetSecs) {
974
+ throw new CoSignUnavailableError(
975
+ `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)`
976
+ );
977
+ }
978
+ const deadlineMs = Math.min(
979
+ Date.now() + ceremonyTimeoutMs,
980
+ (validBeforeSecs - clockSkewBudgetSecs) * 1e3
981
+ );
982
+ let lastTransient;
983
+ for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
984
+ if (Date.now() >= deadlineMs) break;
985
+ const env = {
986
+ ceremonyId: globalThis.crypto.randomUUID(),
987
+ ssid: globalThis.crypto.randomUUID(),
988
+ intent,
989
+ freshnessTs: Math.floor(Date.now() / 1e3),
990
+ freshnessNonce: viem.toHex(globalThis.crypto.getRandomValues(new Uint8Array(16)))
991
+ };
992
+ const authTag = await authenticator.tag(canonicalRequestBytes(env));
942
993
  try {
943
- await conn.close();
944
- } catch {
994
+ const conn = await openConnection(transport);
995
+ try {
996
+ return await runCeremony({
997
+ conn,
998
+ agent,
999
+ sessionId: session.id,
1000
+ env,
1001
+ digest,
1002
+ authTag,
1003
+ from,
1004
+ deadlineMs,
1005
+ maxFrameBytes
1006
+ });
1007
+ } finally {
1008
+ try {
1009
+ await conn.close();
1010
+ } catch {
1011
+ }
1012
+ }
1013
+ } catch (error) {
1014
+ if (error instanceof CoSignUnavailableError && error.transient && attempt < maxAttempts) {
1015
+ lastTransient = error;
1016
+ continue;
1017
+ }
1018
+ throw error;
945
1019
  }
946
1020
  }
1021
+ throw lastTransient ?? new CoSignUnavailableError(
1022
+ "co-sign ceremony budget exhausted before validBefore (W11) \u2014 no attempt could start"
1023
+ );
947
1024
  },
948
1025
  describe() {
949
1026
  return {
@@ -962,23 +1039,32 @@ async function openConnection(transport) {
962
1039
  try {
963
1040
  return await transport.connect();
964
1041
  } catch (cause) {
965
- throw new CoSignUnavailableError("co-signer connection failed", { cause });
1042
+ throw new CoSignUnavailableError("co-signer connection failed", { cause, transient: true });
966
1043
  }
967
1044
  }
968
- async function runCeremony(conn, agent, sessionId, env, digest, authTag) {
1045
+ async function runCeremony(ctx) {
1046
+ const { conn, agent, env, digest, from } = ctx;
969
1047
  const send = async (frame) => {
970
1048
  try {
971
- await conn.send(frame);
1049
+ await withDeadline(conn.send(frame), ctx.deadlineMs);
972
1050
  } catch (cause) {
973
- throw new CoSignUnavailableError("co-sign connection dropped while sending", { cause });
1051
+ if (cause instanceof CoSignUnavailableError) throw cause;
1052
+ throw new CoSignUnavailableError("co-sign connection dropped while sending", {
1053
+ cause,
1054
+ transient: true
1055
+ });
974
1056
  }
975
1057
  };
976
1058
  const recv = async () => {
977
1059
  let frame;
978
1060
  try {
979
- frame = await conn.recv();
1061
+ frame = await withDeadline(conn.recv(), ctx.deadlineMs);
980
1062
  } catch (cause) {
981
- throw new CoSignUnavailableError("co-sign connection dropped mid-ceremony", { cause });
1063
+ if (cause instanceof CoSignUnavailableError) throw cause;
1064
+ throw new CoSignUnavailableError("co-sign connection dropped mid-ceremony", {
1065
+ cause,
1066
+ transient: true
1067
+ });
982
1068
  }
983
1069
  if (frame.wire_version !== WIRE_VERSION) {
984
1070
  throw new CoSignUnavailableError(
@@ -990,13 +1076,13 @@ async function runCeremony(conn, agent, sessionId, env, digest, authTag) {
990
1076
  await send({
991
1077
  wire_version: WIRE_VERSION,
992
1078
  kind: "request",
993
- session_id: sessionId,
1079
+ session_id: ctx.sessionId,
994
1080
  ceremony_id: env.ceremonyId,
995
1081
  ssid: env.ssid,
996
1082
  intent: toWireIntent(env.intent),
997
1083
  freshness_ts: env.freshnessTs,
998
1084
  freshness_nonce: env.freshnessNonce,
999
- auth_tag: authTag
1085
+ auth_tag: ctx.authTag
1000
1086
  });
1001
1087
  let first;
1002
1088
  try {
@@ -1010,6 +1096,12 @@ async function runCeremony(conn, agent, sessionId, env, digest, authTag) {
1010
1096
  const frame = await recv();
1011
1097
  switch (frame.kind) {
1012
1098
  case "round": {
1099
+ const payloadBytes = (frame.payload.length - 2) / 2;
1100
+ if (payloadBytes > ctx.maxFrameBytes) {
1101
+ throw new CoSignUnavailableError(
1102
+ `co-signer sent a ${payloadBytes}-byte round frame (bound ${ctx.maxFrameBytes}) \u2014 refused pre-decode`
1103
+ );
1104
+ }
1013
1105
  let step;
1014
1106
  try {
1015
1107
  step = agent.step(frame.payload);
@@ -1024,15 +1116,16 @@ async function runCeremony(conn, agent, sessionId, env, digest, authTag) {
1024
1116
  break;
1025
1117
  }
1026
1118
  case "result": {
1119
+ const signature = assembleSignature({ r: frame.r, s: frame.s, v: frame.v });
1027
1120
  if (agentSig === null) {
1028
- throw new CoSignUnavailableError(
1029
- "the backend returned a result before the agent derived a signature"
1030
- );
1121
+ await verifyRecovers(digest, signature, frame.s, from, "idempotent-replay");
1122
+ return { ok: true, signature, intent: env.intent };
1031
1123
  }
1032
1124
  if (frame.r.toLowerCase() !== agentSig.r.toLowerCase() || frame.s.toLowerCase() !== agentSig.s.toLowerCase() || frame.v !== agentSig.v) {
1033
1125
  throw new CoSignUnavailableError("the backend and agent derived different signatures");
1034
1126
  }
1035
- return { ok: true, signature: assembleSignature(agentSig), intent: env.intent };
1127
+ await verifyRecovers(digest, signature, frame.s, from, "co-signed");
1128
+ return { ok: true, signature, intent: env.intent };
1036
1129
  }
1037
1130
  case "rejection": {
1038
1131
  if (POLICY_REASONS.has(frame.reason)) {
@@ -1051,6 +1144,52 @@ async function runCeremony(conn, agent, sessionId, env, digest, authTag) {
1051
1144
  }
1052
1145
  }
1053
1146
  }
1147
+ function withDeadline(promise, deadlineMs) {
1148
+ const remaining = deadlineMs - Date.now();
1149
+ if (remaining <= 0) {
1150
+ return Promise.reject(
1151
+ new CoSignUnavailableError(
1152
+ "co-sign ceremony timed out (the deadline fires before validBefore \u2014 W11)"
1153
+ )
1154
+ );
1155
+ }
1156
+ return new Promise((resolve, reject) => {
1157
+ const timer = setTimeout(() => {
1158
+ reject(
1159
+ new CoSignUnavailableError(
1160
+ "co-sign ceremony timed out (the deadline fires before validBefore \u2014 W11)"
1161
+ )
1162
+ );
1163
+ }, remaining);
1164
+ promise.then(
1165
+ (value) => {
1166
+ clearTimeout(timer);
1167
+ resolve(value);
1168
+ },
1169
+ (cause) => {
1170
+ clearTimeout(timer);
1171
+ reject(cause);
1172
+ }
1173
+ );
1174
+ });
1175
+ }
1176
+ var SECP256K1_HALF_N = 0x7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0n;
1177
+ async function verifyRecovers(digest, signature, s, from, what) {
1178
+ if (BigInt(s) > SECP256K1_HALF_N) {
1179
+ throw new CoSignUnavailableError(`the ${what} signature is not low-S (EIP-2)`);
1180
+ }
1181
+ let recovered;
1182
+ try {
1183
+ recovered = await viem.recoverAddress({ hash: digest, signature });
1184
+ } catch (cause) {
1185
+ throw new CoSignUnavailableError(`the ${what} signature failed to recover`, { cause });
1186
+ }
1187
+ if (viem.getAddress(recovered) !== from) {
1188
+ throw new CoSignUnavailableError(
1189
+ `the ${what} signature recovers to ${viem.getAddress(recovered)}, not the group EOA ${from}`
1190
+ );
1191
+ }
1192
+ }
1054
1193
  function assembleSignature(sig) {
1055
1194
  const yParity = sig.v - 27;
1056
1195
  if (yParity !== 0 && yParity !== 1) {