@bolloon/bolloon-agent 0.4.32 → 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.
Files changed (157) hide show
  1. package/dist/agents/chain/explorer.js +52 -0
  2. package/dist/agents/chain/index.js +2 -0
  3. package/dist/agents/chain/onchain-trade.js +15 -1
  4. package/dist/agents/contacts/chain.js +27 -7
  5. package/dist/agents/execution-supervisor.js +493 -35
  6. package/dist/agents/external-events.js +35 -6
  7. package/dist/agents/gateway-group.js +100 -12
  8. package/dist/agents/goal-criteria.js +10 -3
  9. package/dist/agents/goal-flywheel/block-executor.js +346 -0
  10. package/dist/agents/goal-flywheel/continuation-decision.js +592 -0
  11. package/dist/agents/goal-flywheel/goal-change.js +754 -0
  12. package/dist/agents/goal-flywheel/index.js +26 -0
  13. package/dist/agents/goal-flywheel/memory-layers.js +227 -0
  14. package/dist/agents/goal-flywheel/run-closure.js +809 -0
  15. package/dist/agents/goal-flywheel/skill-candidate.js +628 -0
  16. package/dist/agents/goal-flywheel/types.js +329 -0
  17. package/dist/agents/goal-flywheel/wiring/change.js +288 -0
  18. package/dist/agents/goal-flywheel/wiring/closure.js +697 -0
  19. package/dist/agents/goal-flywheel/wiring/continuation.js +432 -0
  20. package/dist/agents/goal-flywheel/wiring/contract.js +270 -0
  21. package/dist/agents/goal-flywheel/wiring/index.js +26 -0
  22. package/dist/agents/goal-flywheel/wiring/monitor.js +254 -0
  23. package/dist/agents/goal-flywheel/wiring/seams.js +592 -0
  24. package/dist/agents/goal-flywheel/work-contract.js +478 -0
  25. package/dist/agents/goal-flywheel/work-monitor.js +488 -0
  26. package/dist/agents/goal-flywheel-wiring.js +1870 -0
  27. package/dist/agents/goal-state-reducer.js +406 -0
  28. package/dist/agents/goal-store.js +35 -1
  29. package/dist/agents/mobile-flywheel-view.js +144 -0
  30. package/dist/agents/mobile-task-actions-verify.js +74 -0
  31. package/dist/agents/mobile-task-actions.js +178 -0
  32. package/dist/agents/mobile-task-views.js +199 -0
  33. package/dist/agents/network-pulse.js +525 -27
  34. package/dist/agents/pi-sdk.js +75 -20
  35. package/dist/agents/run-store.js +18 -5
  36. package/dist/agents/skill-readiness.js +52 -10
  37. package/dist/agents/subagent-manager.js +209 -3
  38. package/dist/agents/task/task-onchain-runner.js +19 -1
  39. package/dist/agents/task/task-runner.js +36 -6
  40. package/dist/agents/task-board.js +702 -0
  41. package/dist/agents/task-group.js +524 -0
  42. package/dist/agents/task-public-text.js +89 -0
  43. package/dist/cli/commands/index.js +3 -0
  44. package/dist/cli/commands/tasks.js +977 -8
  45. package/dist/cli/identity-command.js +177 -0
  46. package/dist/cli/protocol-envelope.js +4 -0
  47. package/dist/cli/setup-wizard.js +71 -0
  48. package/dist/cli/update-commands.js +150 -24
  49. package/dist/cli-entry.js +12 -2
  50. package/dist/electron-build/agents/chain/explorer.js +59 -0
  51. package/dist/electron-build/agents/chain/explorer.js.map +1 -0
  52. package/dist/electron-build/agents/contacts/chain.js +26 -6
  53. package/dist/electron-build/agents/contacts/chain.js.map +1 -1
  54. package/dist/electron-build/agents/execution-supervisor.js +492 -34
  55. package/dist/electron-build/agents/execution-supervisor.js.map +1 -1
  56. package/dist/electron-build/agents/external-events.js +34 -5
  57. package/dist/electron-build/agents/external-events.js.map +1 -1
  58. package/dist/electron-build/agents/goal-criteria.js +10 -3
  59. package/dist/electron-build/agents/goal-criteria.js.map +1 -1
  60. package/dist/electron-build/agents/goal-flywheel/continuation-decision.js +598 -0
  61. package/dist/electron-build/agents/goal-flywheel/continuation-decision.js.map +1 -0
  62. package/dist/electron-build/agents/goal-flywheel/goal-change.js +773 -0
  63. package/dist/electron-build/agents/goal-flywheel/goal-change.js.map +1 -0
  64. package/dist/electron-build/agents/goal-flywheel/memory-layers.js +269 -0
  65. package/dist/electron-build/agents/goal-flywheel/memory-layers.js.map +1 -0
  66. package/dist/electron-build/agents/goal-flywheel/run-closure.js +818 -0
  67. package/dist/electron-build/agents/goal-flywheel/run-closure.js.map +1 -0
  68. package/dist/electron-build/agents/goal-flywheel/skill-candidate.js +642 -0
  69. package/dist/electron-build/agents/goal-flywheel/skill-candidate.js.map +1 -0
  70. package/dist/electron-build/agents/goal-flywheel/types.js +333 -0
  71. package/dist/electron-build/agents/goal-flywheel/types.js.map +1 -0
  72. package/dist/electron-build/agents/goal-flywheel/wiring/change.js +294 -0
  73. package/dist/electron-build/agents/goal-flywheel/wiring/change.js.map +1 -0
  74. package/dist/electron-build/agents/goal-flywheel/wiring/closure.js +742 -0
  75. package/dist/electron-build/agents/goal-flywheel/wiring/closure.js.map +1 -0
  76. package/dist/electron-build/agents/goal-flywheel/wiring/continuation.js +444 -0
  77. package/dist/electron-build/agents/goal-flywheel/wiring/continuation.js.map +1 -0
  78. package/dist/electron-build/agents/goal-flywheel/wiring/contract.js +279 -0
  79. package/dist/electron-build/agents/goal-flywheel/wiring/contract.js.map +1 -0
  80. package/dist/electron-build/agents/goal-flywheel/wiring/index.js +111 -0
  81. package/dist/electron-build/agents/goal-flywheel/wiring/index.js.map +1 -0
  82. package/dist/electron-build/agents/goal-flywheel/wiring/monitor.js +264 -0
  83. package/dist/electron-build/agents/goal-flywheel/wiring/monitor.js.map +1 -0
  84. package/dist/electron-build/agents/goal-flywheel/wiring/seams.js +612 -0
  85. package/dist/electron-build/agents/goal-flywheel/wiring/seams.js.map +1 -0
  86. package/dist/electron-build/agents/goal-flywheel/work-contract.js +488 -0
  87. package/dist/electron-build/agents/goal-flywheel/work-contract.js.map +1 -0
  88. package/dist/electron-build/agents/goal-flywheel/work-monitor.js +501 -0
  89. package/dist/electron-build/agents/goal-flywheel/work-monitor.js.map +1 -0
  90. package/dist/electron-build/agents/goal-flywheel-wiring.js +1962 -0
  91. package/dist/electron-build/agents/goal-flywheel-wiring.js.map +1 -0
  92. package/dist/electron-build/agents/goal-state-reducer.js +414 -0
  93. package/dist/electron-build/agents/goal-state-reducer.js.map +1 -0
  94. package/dist/electron-build/agents/goal-store.js +35 -1
  95. package/dist/electron-build/agents/goal-store.js.map +1 -1
  96. package/dist/electron-build/agents/local-signer.js +101 -0
  97. package/dist/electron-build/agents/local-signer.js.map +1 -0
  98. package/dist/electron-build/agents/network-pulse.js +531 -28
  99. package/dist/electron-build/agents/network-pulse.js.map +1 -1
  100. package/dist/electron-build/agents/pi-sdk.js +74 -19
  101. package/dist/electron-build/agents/pi-sdk.js.map +1 -1
  102. package/dist/electron-build/agents/run-store.js +20 -5
  103. package/dist/electron-build/agents/run-store.js.map +1 -1
  104. package/dist/electron-build/agents/skill-readiness.js +52 -9
  105. package/dist/electron-build/agents/skill-readiness.js.map +1 -1
  106. package/dist/electron-build/agents/task/task-runner.js +35 -5
  107. package/dist/electron-build/agents/task/task-runner.js.map +1 -1
  108. package/dist/electron-build/agents/task-board.js +761 -0
  109. package/dist/electron-build/agents/task-board.js.map +1 -0
  110. package/dist/electron-build/agents/task-contract.js +382 -0
  111. package/dist/electron-build/agents/task-contract.js.map +1 -0
  112. package/dist/electron-build/orbitdb/cid-database.js +83 -17
  113. package/dist/electron-build/orbitdb/cid-database.js.map +1 -1
  114. package/dist/electron-build/orbitdb/ipfs-node.js +31 -2
  115. package/dist/electron-build/orbitdb/ipfs-node.js.map +1 -1
  116. package/dist/electron-build/utils/dual-source.js +471 -0
  117. package/dist/electron-build/utils/dual-source.js.map +1 -0
  118. package/dist/electron-build/utils/update-manager.js +331 -57
  119. package/dist/electron-build/utils/update-manager.js.map +1 -1
  120. package/dist/electron-build/utils/update-state.js +16 -1
  121. package/dist/electron-build/utils/update-state.js.map +1 -1
  122. package/dist/electron-build/utils/version-info.js +82 -2
  123. package/dist/electron-build/utils/version-info.js.map +1 -1
  124. package/dist/index.js +103 -12
  125. package/dist/ios/chain-explorer.js +617 -0
  126. package/dist/ios/explorer.html +163 -0
  127. package/dist/ios/index.html +96 -0
  128. package/dist/ios/mobile-agent.js +2 -2
  129. package/dist/ios/mobile-contacts.js +329 -0
  130. package/dist/ios/mobile-core.js +1379 -226
  131. package/dist/ios/mobile-privacy.js +185 -0
  132. package/dist/ios/mobile.css +19 -0
  133. package/dist/ios/mobile.html +96 -0
  134. package/dist/ios/mobile.js +706 -7
  135. package/dist/ios/routes-chain-index.js +108 -0
  136. package/dist/ios/routes-contacts.js +420 -0
  137. package/dist/ios/routes-onchain-trade.js +99 -0
  138. package/dist/ios/routes-x402-info.js +1 -0
  139. package/dist/ios/server.js +762 -0
  140. package/dist/ios/task-frame-server.js +211 -0
  141. package/dist/orbitdb/cid-database.js +81 -16
  142. package/dist/orbitdb/ipfs-node.js +31 -2
  143. package/dist/utils/dual-source.js +426 -0
  144. package/dist/utils/update-cli.js +16 -21
  145. package/dist/utils/update-health.js +35 -5
  146. package/dist/utils/update-manager.js +329 -57
  147. package/dist/utils/update-state.js +16 -1
  148. package/dist/utils/version-info.js +75 -1
  149. package/dist/web/mobile-contacts.js +44 -11
  150. package/dist/web/mobile-core.js +1197 -513
  151. package/dist/web/mobile-tasks.js +339 -0
  152. package/dist/web/mobile.css +19 -0
  153. package/dist/web/mobile.html +64 -0
  154. package/dist/web/mobile.js +531 -1
  155. package/dist/web/routes-mobile-tasks.js +353 -0
  156. package/dist/web/server.js +163 -4
  157. package/package.json +3 -1
@@ -1374,7 +1374,7 @@
1374
1374
  }
1375
1375
  function encode(data, alphabet4, bitsPerChar) {
1376
1376
  const pad2 = alphabet4[alphabet4.length - 1] === "=";
1377
- const mask = (1 << bitsPerChar) - 1;
1377
+ const mask2 = (1 << bitsPerChar) - 1;
1378
1378
  let out = "";
1379
1379
  let bits = 0;
1380
1380
  let buffer6 = 0;
@@ -1383,11 +1383,11 @@
1383
1383
  bits += 8;
1384
1384
  while (bits > bitsPerChar) {
1385
1385
  bits -= bitsPerChar;
1386
- out += alphabet4[mask & buffer6 >> bits];
1386
+ out += alphabet4[mask2 & buffer6 >> bits];
1387
1387
  }
1388
1388
  }
1389
1389
  if (bits !== 0) {
1390
- out += alphabet4[mask & buffer6 << bitsPerChar - bits];
1390
+ out += alphabet4[mask2 & buffer6 << bitsPerChar - bits];
1391
1391
  }
1392
1392
  if (pad2) {
1393
1393
  while ((out.length * bitsPerChar & 7) !== 0) {
@@ -3361,8 +3361,8 @@
3361
3361
  }
3362
3362
  function encodeInteger(value2) {
3363
3363
  const contents = new Uint8ArrayList();
3364
- const mask = 128;
3365
- const positive2 = (value2.subarray()[0] & mask) === mask;
3364
+ const mask2 = 128;
3365
+ const positive2 = (value2.subarray()[0] & mask2) === mask2;
3366
3366
  if (positive2) {
3367
3367
  contents.append(Uint8Array.from([0]));
3368
3368
  }
@@ -4974,13 +4974,13 @@
4974
4974
  const windows = Math.ceil(scalarBits2 / W2) + 1;
4975
4975
  const windowSize = 2 ** (W2 - 1);
4976
4976
  const maxNumber = 2 ** W2;
4977
- const mask = bitMask(W2);
4977
+ const mask2 = bitMask(W2);
4978
4978
  const shiftBy = BigInt(W2);
4979
- return { windows, windowSize, mask, maxNumber, shiftBy };
4979
+ return { windows, windowSize, mask: mask2, maxNumber, shiftBy };
4980
4980
  }
4981
4981
  function calcOffsets(n, window2, wOpts) {
4982
- const { windowSize, mask, maxNumber, shiftBy } = wOpts;
4983
- let wbits = Number(n & mask);
4982
+ const { windowSize, mask: mask2, maxNumber, shiftBy } = wOpts;
4983
+ let wbits = Number(n & mask2);
4984
4984
  let nextN = n >> shiftBy;
4985
4985
  if (wbits > windowSize) {
4986
4986
  wbits -= maxNumber;
@@ -6801,18 +6801,18 @@
6801
6801
  * Zig-zag encodes this long bits
6802
6802
  */
6803
6803
  zzEncode() {
6804
- const mask = this.hi >> 31;
6805
- this.hi = ((this.hi << 1 | this.lo >>> 31) ^ mask) >>> 0;
6806
- this.lo = (this.lo << 1 ^ mask) >>> 0;
6804
+ const mask2 = this.hi >> 31;
6805
+ this.hi = ((this.hi << 1 | this.lo >>> 31) ^ mask2) >>> 0;
6806
+ this.lo = (this.lo << 1 ^ mask2) >>> 0;
6807
6807
  return this;
6808
6808
  }
6809
6809
  /**
6810
6810
  * Zig-zag decodes this long bits
6811
6811
  */
6812
6812
  zzDecode() {
6813
- const mask = -(this.lo & 1);
6814
- this.lo = ((this.lo >>> 1 | this.hi << 31) ^ mask) >>> 0;
6815
- this.hi = (this.hi >>> 1 ^ mask) >>> 0;
6813
+ const mask2 = -(this.lo & 1);
6814
+ this.lo = ((this.lo >>> 1 | this.hi << 31) ^ mask2) >>> 0;
6815
+ this.hi = (this.hi >>> 1 ^ mask2) >>> 0;
6816
6816
  return this;
6817
6817
  }
6818
6818
  /**
@@ -12897,9 +12897,9 @@ ${indent}[Error list was empty]`;
12897
12897
  }
12898
12898
  }
12899
12899
  }
12900
- function simpleMaskLength(mask) {
12900
+ function simpleMaskLength(mask2) {
12901
12901
  let ones = 0;
12902
- for (let [index, byte] of mask.entries()) {
12902
+ for (let [index, byte] of mask2.entries()) {
12903
12903
  if (byte === 255) {
12904
12904
  ones += 8;
12905
12905
  continue;
@@ -12911,8 +12911,8 @@ ${indent}[Error list was empty]`;
12911
12911
  if ((byte & 128) != 0) {
12912
12912
  return -1;
12913
12913
  }
12914
- for (let i = index + 1; i < mask.length; i++) {
12915
- if (mask[i] != 0) {
12914
+ for (let i = index + 1; i < mask2.length; i++) {
12915
+ if (mask2[i] != 0) {
12916
12916
  return -1;
12917
12917
  }
12918
12918
  }
@@ -12920,9 +12920,9 @@ ${indent}[Error list was empty]`;
12920
12920
  }
12921
12921
  return ones;
12922
12922
  }
12923
- function maskToHex(mask) {
12923
+ function maskToHex(mask2) {
12924
12924
  let hex = "0x";
12925
- for (const byte of mask) {
12925
+ for (const byte of mask2) {
12926
12926
  hex += (byte >> 4).toString(16) + (byte & 15).toString(16);
12927
12927
  }
12928
12928
  return hex;
@@ -12934,20 +12934,20 @@ ${indent}[Error list was empty]`;
12934
12934
  });
12935
12935
 
12936
12936
  // node_modules/@chainsafe/netmask/dist/src/ip.js
12937
- function maskIp(ip, mask) {
12938
- if (mask.length === IPv6Len && ip.length === IPv4Len && allFF(mask, 0, 11)) {
12939
- mask = mask.slice(12);
12937
+ function maskIp(ip, mask2) {
12938
+ if (mask2.length === IPv6Len && ip.length === IPv4Len && allFF(mask2, 0, 11)) {
12939
+ mask2 = mask2.slice(12);
12940
12940
  }
12941
- if (mask.length === IPv4Len && ip.length === IPv6Len && deepEqual(ip, ipv4Prefix, 0, 11)) {
12941
+ if (mask2.length === IPv4Len && ip.length === IPv6Len && deepEqual(ip, ipv4Prefix, 0, 11)) {
12942
12942
  ip = ip.slice(12);
12943
12943
  }
12944
12944
  const n = ip.length;
12945
- if (n != mask.length) {
12945
+ if (n != mask2.length) {
12946
12946
  throw new Error("Failed to mask ip");
12947
12947
  }
12948
12948
  const out = new Uint8Array(n);
12949
12949
  for (let i = 0; i < n; i++) {
12950
- out[i] = ip[i] & mask[i];
12950
+ out[i] = ip[i] & mask2[i];
12951
12951
  }
12952
12952
  return out;
12953
12953
  }
@@ -13009,10 +13009,10 @@ ${indent}[Error list was empty]`;
13009
13009
  if (Number.isNaN(m2) || String(m2).length !== maskString.length || m2 < 0 || m2 > ipLength * 8) {
13010
13010
  throw new Error("Failed to parse given CIDR: " + s2);
13011
13011
  }
13012
- const mask = cidrMask(m2, 8 * ipLength);
13012
+ const mask2 = cidrMask(m2, 8 * ipLength);
13013
13013
  return {
13014
- network: maskIp(ip, mask),
13015
- mask
13014
+ network: maskIp(ip, mask2),
13015
+ mask: mask2
13016
13016
  };
13017
13017
  }
13018
13018
  function cidrMask(ones, bits) {
@@ -13054,18 +13054,18 @@ ${indent}[Error list was empty]`;
13054
13054
  * @param ipOrCidr either network ip or full cidr address
13055
13055
  * @param mask in case ipOrCidr is network this can be either mask in decimal format or as ip address
13056
13056
  */
13057
- constructor(ipOrCidr, mask) {
13058
- if (mask == null) {
13057
+ constructor(ipOrCidr, mask2) {
13058
+ if (mask2 == null) {
13059
13059
  ({ network: this.network, mask: this.mask } = parseCidr(ipOrCidr));
13060
13060
  } else {
13061
13061
  const ipResult = parseIP(ipOrCidr);
13062
13062
  if (ipResult == null) {
13063
13063
  throw new Error("Failed to parse network");
13064
13064
  }
13065
- mask = String(mask);
13066
- const m2 = parseInt(mask, 10);
13067
- if (Number.isNaN(m2) || String(m2).length !== mask.length || m2 < 0 || m2 > ipResult.length * 8) {
13068
- const maskResult = parseIP(mask);
13065
+ mask2 = String(mask2);
13066
+ const m2 = parseInt(mask2, 10);
13067
+ if (Number.isNaN(m2) || String(m2).length !== mask2.length || m2 < 0 || m2 > ipResult.length * 8) {
13068
+ const maskResult = parseIP(mask2);
13069
13069
  if (maskResult == null) {
13070
13070
  throw new Error("Failed to parse mask");
13071
13071
  }
@@ -13087,8 +13087,8 @@ ${indent}[Error list was empty]`;
13087
13087
  /**Serializes back to string format */
13088
13088
  toString() {
13089
13089
  const l = simpleMaskLength(this.mask);
13090
- const mask = l !== -1 ? String(l) : maskToHex(this.mask);
13091
- return ipToString(this.network) + "/" + mask;
13090
+ const mask2 = l !== -1 ? String(l) : maskToHex(this.mask);
13091
+ return ipToString(this.network) + "/" + mask2;
13092
13092
  }
13093
13093
  };
13094
13094
  }
@@ -13309,11 +13309,11 @@ ${indent}[Error list was empty]`;
13309
13309
  }
13310
13310
  }
13311
13311
  var Netmask4Impl = class _Netmask4Impl {
13312
- constructor(net, mask) {
13312
+ constructor(net, mask2) {
13313
13313
  if (typeof net !== "string") {
13314
13314
  throw new Error("Missing `net' parameter");
13315
13315
  }
13316
- let maskStr = mask;
13316
+ let maskStr = mask2;
13317
13317
  if (!maskStr) {
13318
13318
  const parts = net.split("/", 2);
13319
13319
  net = parts[0];
@@ -13498,11 +13498,11 @@ ${indent}[Error list was empty]`;
13498
13498
  }
13499
13499
  }
13500
13500
  var Netmask6Impl = class _Netmask6Impl {
13501
- constructor(net, mask) {
13501
+ constructor(net, mask2) {
13502
13502
  if (typeof net !== "string") {
13503
13503
  throw new Error("Missing `net' parameter");
13504
13504
  }
13505
- let prefixLen = mask;
13505
+ let prefixLen = mask2;
13506
13506
  if (prefixLen === void 0 || prefixLen === null) {
13507
13507
  const slashIdx = net.indexOf("/");
13508
13508
  if (slashIdx !== -1) {
@@ -13585,15 +13585,15 @@ ${indent}[Error list was empty]`;
13585
13585
  } });
13586
13586
  var netmask6_1 = require_netmask6();
13587
13587
  var Netmask2 = class _Netmask {
13588
- constructor(net, mask) {
13588
+ constructor(net, mask2) {
13589
13589
  if (typeof net !== "string") {
13590
13590
  throw new Error("Missing `net' parameter");
13591
13591
  }
13592
13592
  const addrPart = net.indexOf("/") !== -1 ? net.substring(0, net.indexOf("/")) : net;
13593
13593
  if (addrPart.indexOf(":") !== -1) {
13594
- this._impl = new netmask6_1.Netmask6Impl(net, mask);
13594
+ this._impl = new netmask6_1.Netmask6Impl(net, mask2);
13595
13595
  } else {
13596
- this._impl = new netmask4_1.Netmask4Impl(net, mask);
13596
+ this._impl = new netmask4_1.Netmask4Impl(net, mask2);
13597
13597
  }
13598
13598
  this.base = this._impl.base;
13599
13599
  this.mask = this._impl.mask;
@@ -18414,11 +18414,11 @@ ${indent}[Error list was empty]`;
18414
18414
  function toToken2(data, pos, prefix, length20, options2) {
18415
18415
  const totLength = prefix + length20;
18416
18416
  assertEnoughData(data, pos, totLength);
18417
- const tok = new Token(Type.string, toStr(data, pos + prefix, pos + totLength), totLength);
18417
+ const tok2 = new Token(Type.string, toStr(data, pos + prefix, pos + totLength), totLength);
18418
18418
  if (options2.retainStringBytes === true) {
18419
- tok.byteValue = data.slice(pos + prefix, pos + totLength);
18419
+ tok2.byteValue = data.slice(pos + prefix, pos + totLength);
18420
18420
  }
18421
- return tok;
18421
+ return tok2;
18422
18422
  }
18423
18423
  function decodeStringCompact(data, pos, minor, options2) {
18424
18424
  return toToken2(data, pos, 1, minor, options2);
@@ -21481,18 +21481,18 @@ ${indent}[Error list was empty]`;
21481
21481
  * Zig-zag encodes this long bits
21482
21482
  */
21483
21483
  zzEncode() {
21484
- const mask = this.hi >> 31;
21485
- this.hi = ((this.hi << 1 | this.lo >>> 31) ^ mask) >>> 0;
21486
- this.lo = (this.lo << 1 ^ mask) >>> 0;
21484
+ const mask2 = this.hi >> 31;
21485
+ this.hi = ((this.hi << 1 | this.lo >>> 31) ^ mask2) >>> 0;
21486
+ this.lo = (this.lo << 1 ^ mask2) >>> 0;
21487
21487
  return this;
21488
21488
  }
21489
21489
  /**
21490
21490
  * Zig-zag decodes this long bits
21491
21491
  */
21492
21492
  zzDecode() {
21493
- const mask = -(this.lo & 1);
21494
- this.lo = ((this.lo >>> 1 | this.hi << 31) ^ mask) >>> 0;
21495
- this.hi = (this.hi >>> 1 ^ mask) >>> 0;
21493
+ const mask2 = -(this.lo & 1);
21494
+ this.lo = ((this.lo >>> 1 | this.hi << 31) ^ mask2) >>> 0;
21495
+ this.hi = (this.hi >>> 1 ^ mask2) >>> 0;
21496
21496
  return this;
21497
21497
  }
21498
21498
  /**
@@ -24141,18 +24141,18 @@ ${indent}[Error list was empty]`;
24141
24141
  * Zig-zag encodes this long bits
24142
24142
  */
24143
24143
  zzEncode() {
24144
- const mask = this.hi >> 31;
24145
- this.hi = ((this.hi << 1 | this.lo >>> 31) ^ mask) >>> 0;
24146
- this.lo = (this.lo << 1 ^ mask) >>> 0;
24144
+ const mask2 = this.hi >> 31;
24145
+ this.hi = ((this.hi << 1 | this.lo >>> 31) ^ mask2) >>> 0;
24146
+ this.lo = (this.lo << 1 ^ mask2) >>> 0;
24147
24147
  return this;
24148
24148
  }
24149
24149
  /**
24150
24150
  * Zig-zag decodes this long bits
24151
24151
  */
24152
24152
  zzDecode() {
24153
- const mask = -(this.lo & 1);
24154
- this.lo = ((this.lo >>> 1 | this.hi << 31) ^ mask) >>> 0;
24155
- this.hi = (this.hi >>> 1 ^ mask) >>> 0;
24153
+ const mask2 = -(this.lo & 1);
24154
+ this.lo = ((this.lo >>> 1 | this.hi << 31) ^ mask2) >>> 0;
24155
+ this.hi = (this.hi >>> 1 ^ mask2) >>> 0;
24156
24156
  return this;
24157
24157
  }
24158
24158
  /**
@@ -28712,18 +28712,18 @@ ${indent}[Error list was empty]`;
28712
28712
  }
28713
28713
  function multiaddrToIpNet(ma) {
28714
28714
  const config = getNetConfig(ma);
28715
- let mask = config.cidr;
28715
+ let mask2 = config.cidr;
28716
28716
  if (config.type !== "ip4" && config.type !== "ip6") {
28717
28717
  throw new InvalidParametersError(`Multiaddr ${ma} was not an IPv4 or IPv6 address`);
28718
28718
  }
28719
- if (mask == null) {
28719
+ if (mask2 == null) {
28720
28720
  switch (config.type) {
28721
28721
  case "ip4": {
28722
- mask = 32;
28722
+ mask2 = 32;
28723
28723
  break;
28724
28724
  }
28725
28725
  case "ip6": {
28726
- mask = 128;
28726
+ mask2 = 128;
28727
28727
  break;
28728
28728
  }
28729
28729
  default: {
@@ -28731,7 +28731,7 @@ ${indent}[Error list was empty]`;
28731
28731
  }
28732
28732
  }
28733
28733
  }
28734
- return new IpNet(config.host, mask);
28734
+ return new IpNet(config.host, mask2);
28735
28735
  }
28736
28736
  function isDirect(ma) {
28737
28737
  return !Circuit.exactMatch(ma);
@@ -35926,12 +35926,12 @@ ${[...listenStats.errors.entries()].map(([addr, err2]) => {
35926
35926
  g[i] &= 8191;
35927
35927
  }
35928
35928
  g[9] -= 1 << 13;
35929
- let mask = (c ^ 1) - 1;
35929
+ let mask2 = (c ^ 1) - 1;
35930
35930
  for (let i = 0; i < 10; i++)
35931
- g[i] &= mask;
35932
- mask = ~mask;
35931
+ g[i] &= mask2;
35932
+ mask2 = ~mask2;
35933
35933
  for (let i = 0; i < 10; i++)
35934
- h3[i] = h3[i] & mask | g[i];
35934
+ h3[i] = h3[i] & mask2 | g[i];
35935
35935
  h3[0] = (h3[0] | h3[1] << 13) & 65535;
35936
35936
  h3[1] = (h3[1] >>> 3 | h3[2] << 10) & 65535;
35937
35937
  h3[2] = (h3[2] >>> 6 | h3[3] << 7) & 65535;
@@ -36669,7 +36669,7 @@ ${[...listenStats.errors.entries()].map(([addr, err2]) => {
36669
36669
  }
36670
36670
  function encode10(data, alphabet4, bitsPerChar) {
36671
36671
  const pad2 = alphabet4[alphabet4.length - 1] === "=";
36672
- const mask = (1 << bitsPerChar) - 1;
36672
+ const mask2 = (1 << bitsPerChar) - 1;
36673
36673
  let out = "";
36674
36674
  let bits = 0;
36675
36675
  let buffer6 = 0;
@@ -36678,11 +36678,11 @@ ${[...listenStats.errors.entries()].map(([addr, err2]) => {
36678
36678
  bits += 8;
36679
36679
  while (bits > bitsPerChar) {
36680
36680
  bits -= bitsPerChar;
36681
- out += alphabet4[mask & buffer6 >> bits];
36681
+ out += alphabet4[mask2 & buffer6 >> bits];
36682
36682
  }
36683
36683
  }
36684
36684
  if (bits !== 0) {
36685
- out += alphabet4[mask & buffer6 << bitsPerChar - bits];
36685
+ out += alphabet4[mask2 & buffer6 << bitsPerChar - bits];
36686
36686
  }
36687
36687
  if (pad2) {
36688
36688
  while ((out.length * bitsPerChar & 7) !== 0) {
@@ -38978,18 +38978,18 @@ ${[...listenStats.errors.entries()].map(([addr, err2]) => {
38978
38978
  * Zig-zag encodes this long bits
38979
38979
  */
38980
38980
  zzEncode() {
38981
- const mask = this.hi >> 31;
38982
- this.hi = ((this.hi << 1 | this.lo >>> 31) ^ mask) >>> 0;
38983
- this.lo = (this.lo << 1 ^ mask) >>> 0;
38981
+ const mask2 = this.hi >> 31;
38982
+ this.hi = ((this.hi << 1 | this.lo >>> 31) ^ mask2) >>> 0;
38983
+ this.lo = (this.lo << 1 ^ mask2) >>> 0;
38984
38984
  return this;
38985
38985
  }
38986
38986
  /**
38987
38987
  * Zig-zag decodes this long bits
38988
38988
  */
38989
38989
  zzDecode() {
38990
- const mask = -(this.lo & 1);
38991
- this.lo = ((this.lo >>> 1 | this.hi << 31) ^ mask) >>> 0;
38992
- this.hi = (this.hi >>> 1 ^ mask) >>> 0;
38990
+ const mask2 = -(this.lo & 1);
38991
+ this.lo = ((this.lo >>> 1 | this.hi << 31) ^ mask2) >>> 0;
38992
+ this.hi = (this.hi >>> 1 ^ mask2) >>> 0;
38993
38993
  return this;
38994
38994
  }
38995
38995
  /**
@@ -41706,18 +41706,18 @@ ${[...listenStats.errors.entries()].map(([addr, err2]) => {
41706
41706
  * Zig-zag encodes this long bits
41707
41707
  */
41708
41708
  zzEncode() {
41709
- const mask = this.hi >> 31;
41710
- this.hi = ((this.hi << 1 | this.lo >>> 31) ^ mask) >>> 0;
41711
- this.lo = (this.lo << 1 ^ mask) >>> 0;
41709
+ const mask2 = this.hi >> 31;
41710
+ this.hi = ((this.hi << 1 | this.lo >>> 31) ^ mask2) >>> 0;
41711
+ this.lo = (this.lo << 1 ^ mask2) >>> 0;
41712
41712
  return this;
41713
41713
  }
41714
41714
  /**
41715
41715
  * Zig-zag decodes this long bits
41716
41716
  */
41717
41717
  zzDecode() {
41718
- const mask = -(this.lo & 1);
41719
- this.lo = ((this.lo >>> 1 | this.hi << 31) ^ mask) >>> 0;
41720
- this.hi = (this.hi >>> 1 ^ mask) >>> 0;
41718
+ const mask2 = -(this.lo & 1);
41719
+ this.lo = ((this.lo >>> 1 | this.hi << 31) ^ mask2) >>> 0;
41720
+ this.hi = (this.hi >>> 1 ^ mask2) >>> 0;
41721
41721
  return this;
41722
41722
  }
41723
41723
  /**
@@ -43635,18 +43635,18 @@ ${[...listenStats.errors.entries()].map(([addr, err2]) => {
43635
43635
  * Zig-zag encodes this long bits
43636
43636
  */
43637
43637
  zzEncode() {
43638
- const mask = this.hi >> 31;
43639
- this.hi = ((this.hi << 1 | this.lo >>> 31) ^ mask) >>> 0;
43640
- this.lo = (this.lo << 1 ^ mask) >>> 0;
43638
+ const mask2 = this.hi >> 31;
43639
+ this.hi = ((this.hi << 1 | this.lo >>> 31) ^ mask2) >>> 0;
43640
+ this.lo = (this.lo << 1 ^ mask2) >>> 0;
43641
43641
  return this;
43642
43642
  }
43643
43643
  /**
43644
43644
  * Zig-zag decodes this long bits
43645
43645
  */
43646
43646
  zzDecode() {
43647
- const mask = -(this.lo & 1);
43648
- this.lo = ((this.lo >>> 1 | this.hi << 31) ^ mask) >>> 0;
43649
- this.hi = (this.hi >>> 1 ^ mask) >>> 0;
43647
+ const mask2 = -(this.lo & 1);
43648
+ this.lo = ((this.lo >>> 1 | this.hi << 31) ^ mask2) >>> 0;
43649
+ this.hi = (this.hi >>> 1 ^ mask2) >>> 0;
43650
43650
  return this;
43651
43651
  }
43652
43652
  /**
@@ -45169,18 +45169,18 @@ ${[...listenStats.errors.entries()].map(([addr, err2]) => {
45169
45169
  * Zig-zag encodes this long bits
45170
45170
  */
45171
45171
  zzEncode() {
45172
- const mask = this.hi >> 31;
45173
- this.hi = ((this.hi << 1 | this.lo >>> 31) ^ mask) >>> 0;
45174
- this.lo = (this.lo << 1 ^ mask) >>> 0;
45172
+ const mask2 = this.hi >> 31;
45173
+ this.hi = ((this.hi << 1 | this.lo >>> 31) ^ mask2) >>> 0;
45174
+ this.lo = (this.lo << 1 ^ mask2) >>> 0;
45175
45175
  return this;
45176
45176
  }
45177
45177
  /**
45178
45178
  * Zig-zag decodes this long bits
45179
45179
  */
45180
45180
  zzDecode() {
45181
- const mask = -(this.lo & 1);
45182
- this.lo = ((this.lo >>> 1 | this.hi << 31) ^ mask) >>> 0;
45183
- this.hi = (this.hi >>> 1 ^ mask) >>> 0;
45181
+ const mask2 = -(this.lo & 1);
45182
+ this.lo = ((this.lo >>> 1 | this.hi << 31) ^ mask2) >>> 0;
45183
+ this.hi = (this.hi >>> 1 ^ mask2) >>> 0;
45184
45184
  return this;
45185
45185
  }
45186
45186
  /**
@@ -66360,8 +66360,8 @@ ${[...listenStats.errors.entries()].map(([addr, err2]) => {
66360
66360
  const view = new DataView(localKadId.buffer, localKadId.byteOffset, localKadId.byteLength);
66361
66361
  const localPrefix = view.getUint16(0, false);
66362
66362
  const toggledLocalPrefix = localPrefix ^ 32768 >> targetCommonPrefixLength;
66363
- const mask = 65535 << 16 - (targetCommonPrefixLength + 1);
66364
- const targetPrefix = toggledLocalPrefix & mask | randomPrefix & ~mask;
66363
+ const mask2 = 65535 << 16 - (targetCommonPrefixLength + 1);
66364
+ const targetPrefix = toggledLocalPrefix & mask2 | randomPrefix & ~mask2;
66365
66365
  const keyPrefix3 = generated_prefix_list_browser_default[targetPrefix];
66366
66366
  const keyBuffer = new ArrayBuffer(34);
66367
66367
  const keyView = new DataView(keyBuffer, 0, keyBuffer.byteLength);
@@ -67641,18 +67641,18 @@ ${[...listenStats.errors.entries()].map(([addr, err2]) => {
67641
67641
  * Zig-zag encodes this long bits
67642
67642
  */
67643
67643
  zzEncode() {
67644
- const mask = this.hi >> 31;
67645
- this.hi = ((this.hi << 1 | this.lo >>> 31) ^ mask) >>> 0;
67646
- this.lo = (this.lo << 1 ^ mask) >>> 0;
67644
+ const mask2 = this.hi >> 31;
67645
+ this.hi = ((this.hi << 1 | this.lo >>> 31) ^ mask2) >>> 0;
67646
+ this.lo = (this.lo << 1 ^ mask2) >>> 0;
67647
67647
  return this;
67648
67648
  }
67649
67649
  /**
67650
67650
  * Zig-zag decodes this long bits
67651
67651
  */
67652
67652
  zzDecode() {
67653
- const mask = -(this.lo & 1);
67654
- this.lo = ((this.lo >>> 1 | this.hi << 31) ^ mask) >>> 0;
67655
- this.hi = (this.hi >>> 1 ^ mask) >>> 0;
67653
+ const mask2 = -(this.lo & 1);
67654
+ this.lo = ((this.lo >>> 1 | this.hi << 31) ^ mask2) >>> 0;
67655
+ this.hi = (this.hi >>> 1 ^ mask2) >>> 0;
67656
67656
  return this;
67657
67657
  }
67658
67658
  /**
@@ -75209,18 +75209,18 @@ ${[...listenStats.errors.entries()].map(([addr, err2]) => {
75209
75209
  * Zig-zag encodes this long bits
75210
75210
  */
75211
75211
  zzEncode() {
75212
- const mask = this.hi >> 31;
75213
- this.hi = ((this.hi << 1 | this.lo >>> 31) ^ mask) >>> 0;
75214
- this.lo = (this.lo << 1 ^ mask) >>> 0;
75212
+ const mask2 = this.hi >> 31;
75213
+ this.hi = ((this.hi << 1 | this.lo >>> 31) ^ mask2) >>> 0;
75214
+ this.lo = (this.lo << 1 ^ mask2) >>> 0;
75215
75215
  return this;
75216
75216
  }
75217
75217
  /**
75218
75218
  * Zig-zag decodes this long bits
75219
75219
  */
75220
75220
  zzDecode() {
75221
- const mask = -(this.lo & 1);
75222
- this.lo = ((this.lo >>> 1 | this.hi << 31) ^ mask) >>> 0;
75223
- this.hi = (this.hi >>> 1 ^ mask) >>> 0;
75221
+ const mask2 = -(this.lo & 1);
75222
+ this.lo = ((this.lo >>> 1 | this.hi << 31) ^ mask2) >>> 0;
75223
+ this.hi = (this.hi >>> 1 ^ mask2) >>> 0;
75224
75224
  return this;
75225
75225
  }
75226
75226
  /**
@@ -81244,7 +81244,7 @@ ${[...listenStats.errors.entries()].map(([addr, err2]) => {
81244
81244
  let carry = 0;
81245
81245
  let pos = 0;
81246
81246
  const max = powers[from6];
81247
- const mask = powers[to] - 1;
81247
+ const mask2 = powers[to] - 1;
81248
81248
  const res = [];
81249
81249
  for (const n of data) {
81250
81250
  anumber6(n);
@@ -81255,13 +81255,13 @@ ${[...listenStats.errors.entries()].map(([addr, err2]) => {
81255
81255
  throw new Error(`convertRadix2: carry overflow pos=${pos} from=${from6}`);
81256
81256
  pos += from6;
81257
81257
  for (; pos >= to; pos -= to)
81258
- res.push((carry >> pos - to & mask) >>> 0);
81258
+ res.push((carry >> pos - to & mask2) >>> 0);
81259
81259
  const pow = powers[pos];
81260
81260
  if (pow === void 0)
81261
81261
  throw new Error("invalid carry");
81262
81262
  carry &= pow - 1;
81263
81263
  }
81264
- carry = carry << to - pos & mask;
81264
+ carry = carry << to - pos & mask2;
81265
81265
  if (!padding2 && pos >= from6)
81266
81266
  throw new Error("Excess padding");
81267
81267
  if (!padding2 && carry > 0)
@@ -84769,13 +84769,13 @@ zoo`.split("\n");
84769
84769
  const windows = Math.ceil(scalarBits2 / W2) + 1;
84770
84770
  const windowSize = 2 ** (W2 - 1);
84771
84771
  const maxNumber = 2 ** W2;
84772
- const mask = bitMask2(W2);
84772
+ const mask2 = bitMask2(W2);
84773
84773
  const shiftBy = BigInt(W2);
84774
- return { windows, windowSize, mask, maxNumber, shiftBy };
84774
+ return { windows, windowSize, mask: mask2, maxNumber, shiftBy };
84775
84775
  }
84776
84776
  function calcOffsets2(n, window2, wOpts) {
84777
- const { windowSize, mask, maxNumber, shiftBy } = wOpts;
84778
- let wbits = Number(n & mask);
84777
+ const { windowSize, mask: mask2, maxNumber, shiftBy } = wOpts;
84778
+ let wbits = Number(n & mask2);
84779
84779
  let nextN = n >> shiftBy;
84780
84780
  if (wbits > windowSize) {
84781
84781
  wbits -= maxNumber;
@@ -87666,6 +87666,406 @@ ${r.steps.map((s3) => `${s3.ok ? "\u2713" : "\u2717"} ${s3.step}: ${s3.note}`).j
87666
87666
  }
87667
87667
  });
87668
87668
 
87669
+ // src/agents/contacts/grant-payload.ts
87670
+ function canonicalGrantPayload(g) {
87671
+ return JSON.stringify({
87672
+ grantId: g.grantId,
87673
+ identityId: g.identityId,
87674
+ ownerDid: g.ownerDid,
87675
+ level: g.level,
87676
+ channels: g.channels,
87677
+ contactScope: g.contactScope,
87678
+ taskScope: g.taskScope,
87679
+ contentScope: g.contentScope,
87680
+ replyWakeAllowed: g.replyWakeAllowed,
87681
+ autoSend: g.autoSend,
87682
+ sensitiveContentAllowed: g.sensitiveContentAllowed,
87683
+ newRecipientAllowed: g.newRecipientAllowed,
87684
+ grantedAt: g.grantedAt,
87685
+ grantedBy: g.grantedBy,
87686
+ grantedVia: g.grantedVia,
87687
+ deviceIds: [...g.deviceIds].sort(),
87688
+ status: g.status,
87689
+ revokedAt: g.revokedAt || "",
87690
+ grantVersion: g.grantVersion
87691
+ });
87692
+ }
87693
+ function canonicalRevocationPayload(r) {
87694
+ return JSON.stringify({ grantId: r.grantId, grantVersion: r.grantVersion, revokedAt: r.revokedAt, by: r.by });
87695
+ }
87696
+ function presetForChoice(choice) {
87697
+ const base6 = { channels: "both", replyWakeAllowed: true, autoSend: true, newRecipientAllowed: false };
87698
+ switch (choice) {
87699
+ case "task_once":
87700
+ return { ...base6, level: "task_once", contactScope: "verified_contacts", taskScope: "current_goal", contentScope: "normal", autoSend: false, sensitiveContentAllowed: false };
87701
+ case "persistent":
87702
+ return { ...base6, level: "persistent", contactScope: "verified_contacts", taskScope: "all_future_goals", contentScope: "normal", sensitiveContentAllowed: false };
87703
+ case "full_contact_access":
87704
+ return { ...base6, level: "full_contact_access", contactScope: "all_contacts", taskScope: "all_future_goals", contentScope: "sensitive", sensitiveContentAllowed: true };
87705
+ }
87706
+ }
87707
+ var init_grant_payload = __esm({
87708
+ "src/agents/contacts/grant-payload.ts"() {
87709
+ "use strict";
87710
+ }
87711
+ });
87712
+
87713
+ // src/web/mobile-contacts.ts
87714
+ var mobile_contacts_exports = {};
87715
+ __export(mobile_contacts_exports, {
87716
+ authorizeFromPhone: () => authorizeFromPhone,
87717
+ bindFromPhone: () => bindFromPhone,
87718
+ buildPhoneCard: () => buildPhoneCard,
87719
+ decideApprovalFromPhone: () => decideApprovalFromPhone,
87720
+ ed25519Available: () => ed25519Available,
87721
+ flushQueuedGrants: () => flushQueuedGrants,
87722
+ loadCapabilityCopy: () => loadCapabilityCopy,
87723
+ loadFromDesktop: () => loadFromDesktop,
87724
+ loadOrCreateDeviceKey: () => loadOrCreateDeviceKey,
87725
+ pendingQueue: () => pendingQueue,
87726
+ registerDeviceOnDesktop: () => registerDeviceOnDesktop,
87727
+ revokeFromPhone: () => revokeFromPhone,
87728
+ saveCapabilityCopy: () => saveCapabilityCopy,
87729
+ sha256HexOf: () => sha256HexOf,
87730
+ signGrantOnDevice: () => signGrantOnDevice,
87731
+ signPayloadOnDevice: () => signPayloadOnDevice,
87732
+ signPayloadWithMaterial: () => signPayloadWithMaterial,
87733
+ signRevocationOnDevice: () => signRevocationOnDevice,
87734
+ verifyFromPhone: () => verifyFromPhone
87735
+ });
87736
+ function ed25519Available(c) {
87737
+ const cobj = cryptoOf(c);
87738
+ if (!cobj?.subtle) return false;
87739
+ try {
87740
+ const alg = { name: "Ed25519" };
87741
+ return typeof cobj.subtle.importKey === "function" && !!alg;
87742
+ } catch {
87743
+ return false;
87744
+ }
87745
+ }
87746
+ function bytesOf(s2) {
87747
+ const src4 = new TextEncoder().encode(s2);
87748
+ const out = new Uint8Array(new ArrayBuffer(src4.byteLength));
87749
+ out.set(src4);
87750
+ return out;
87751
+ }
87752
+ function b64(buf) {
87753
+ const arr = new Uint8Array(buf);
87754
+ let out = "";
87755
+ for (let i = 0; i < arr.length; i++) out += String.fromCharCode(arr[i]);
87756
+ return btoa(out);
87757
+ }
87758
+ function pemFromSpki(spki) {
87759
+ const b = b64(spki).replace(/(.{64})/g, "$1\n").trim();
87760
+ return `-----BEGIN PUBLIC KEY-----
87761
+ ${b}
87762
+ -----END PUBLIC KEY-----
87763
+ `;
87764
+ }
87765
+ async function sha256Hex(c, text) {
87766
+ const d2 = await c.subtle.digest("SHA-256", bytesOf(text));
87767
+ return [...new Uint8Array(d2)].map((x) => x.toString(16).padStart(2, "0")).join("");
87768
+ }
87769
+ async function loadOrCreateDeviceKey(storage2, c) {
87770
+ const cobj = cryptoOf(c);
87771
+ if (!cobj?.subtle) throw new Error("device_signing_unavailable: \u672C\u673A\u6CA1\u6709 WebCrypto");
87772
+ const raw = storage2.getItem(K_DEVICE);
87773
+ if (raw) {
87774
+ try {
87775
+ const parsed = JSON.parse(raw);
87776
+ if (parsed?.privateKeyJwk && parsed?.publicKeyPem && parsed?.deviceId) return parsed;
87777
+ } catch {
87778
+ }
87779
+ }
87780
+ let pair;
87781
+ try {
87782
+ pair = await cobj.subtle.generateKey({ name: "Ed25519" }, true, ["sign", "verify"]);
87783
+ } catch (err2) {
87784
+ throw new Error(`device_signing_unavailable: ${String(err2?.message || err2)}`);
87785
+ }
87786
+ const publicKeyPem = pemFromSpki(await cobj.subtle.exportKey("spki", pair.publicKey));
87787
+ const privateKeyJwk = await cobj.subtle.exportKey("jwk", pair.privateKey);
87788
+ const deviceId = `dev-${(await sha256Hex(cobj, publicKeyPem)).slice(0, 12)}`;
87789
+ const material = { deviceId, publicKeyPem, privateKeyJwk };
87790
+ storage2.setItem(K_DEVICE, JSON.stringify(material));
87791
+ return material;
87792
+ }
87793
+ async function signGrantOnDevice(grant, storage2, c) {
87794
+ return signPayloadOnDevice(canonicalGrantPayload(grant), storage2, c);
87795
+ }
87796
+ async function signRevocationOnDevice(rev, storage2, c) {
87797
+ return signPayloadOnDevice(canonicalRevocationPayload(rev), storage2, c);
87798
+ }
87799
+ async function signPayloadOnDevice(payload, storage2, c) {
87800
+ const cobj = cryptoOf(c);
87801
+ if (!cobj?.subtle) throw new Error("device_signing_unavailable: \u672C\u673A\u6CA1\u6709 WebCrypto");
87802
+ const material = await loadOrCreateDeviceKey(storage2, cobj);
87803
+ const key = await cobj.subtle.importKey("jwk", material.privateKeyJwk, { name: "Ed25519" }, false, ["sign"]);
87804
+ const bytes = bytesOf(String(payload ?? ""));
87805
+ const sig = await cobj.subtle.sign({ name: "Ed25519" }, key, bytes);
87806
+ return { deviceId: material.deviceId, alg: "ed25519", payloadHash: await sha256Hex(cobj, String(payload ?? "")), sig: b64(sig) };
87807
+ }
87808
+ async function sha256HexOf(text, c) {
87809
+ const cobj = cryptoOf(c);
87810
+ if (!cobj?.subtle) throw new Error("device_signing_unavailable: \u672C\u673A\u6CA1\u6709 WebCrypto");
87811
+ return sha256Hex(cobj, String(text ?? ""));
87812
+ }
87813
+ async function signPayloadWithMaterial(payload, material, c) {
87814
+ const cobj = cryptoOf(c);
87815
+ if (!cobj?.subtle) throw new Error("device_signing_unavailable: \u672C\u673A\u6CA1\u6709 WebCrypto");
87816
+ const key = await cobj.subtle.importKey("jwk", material.privateKeyJwk, { name: "Ed25519" }, false, ["sign"]);
87817
+ const sig = await cobj.subtle.sign({ name: "Ed25519" }, key, bytesOf(String(payload ?? "")));
87818
+ return { deviceId: material.deviceId, alg: "ed25519", payloadHash: await sha256Hex(cobj, String(payload ?? "")), sig: b64(sig) };
87819
+ }
87820
+ async function post(base6, path, body, f) {
87821
+ const res = await f(`${String(base6).replace(/\/+$/, "")}${path}`, {
87822
+ method: "POST",
87823
+ headers: { "Content-Type": "application/json" },
87824
+ body: JSON.stringify(body ?? {})
87825
+ });
87826
+ let json = null;
87827
+ try {
87828
+ json = await res.json();
87829
+ } catch {
87830
+ }
87831
+ return { status: res.status, json };
87832
+ }
87833
+ async function get(base6, path, f) {
87834
+ const res = await f(`${String(base6).replace(/\/+$/, "")}${path}`);
87835
+ let json = null;
87836
+ try {
87837
+ json = await res.json();
87838
+ } catch {
87839
+ }
87840
+ return { status: res.status, json };
87841
+ }
87842
+ async function registerDeviceOnDesktop(base6, storage2, opts = {}) {
87843
+ const f = opts.fetchImpl || fetch;
87844
+ const dev = await loadOrCreateDeviceKey(storage2, opts.cryptoObj);
87845
+ const r = await post(base6, "/api/contacts/devices", { deviceId: dev.deviceId, publicKeyPem: dev.publicKeyPem, label: opts.label || "\u624B\u673A" }, f);
87846
+ if (r.status !== 200 || !r.json?.ok) return { ok: false, error: `\u684C\u9762\u672A\u63A5\u53D7\u8BBE\u5907\u767B\u8BB0: ${r.json?.error || r.status}`, data: dev };
87847
+ return { ok: true, data: dev };
87848
+ }
87849
+ function saveCapabilityCopy(storage2, items) {
87850
+ const clean8 = (items || []).map((c) => ({
87851
+ contactId: String(c.contactId || ""),
87852
+ kind: c.kind,
87853
+ displayValue: String(c.displayValue || ""),
87854
+ verificationStatus: String(c.verificationStatus || ""),
87855
+ capabilities: [...c.capabilities || []],
87856
+ provider: c.provider ? String(c.provider) : void 0,
87857
+ state: c.state ? String(c.state) : void 0
87858
+ }));
87859
+ storage2.setItem(K_CAPS, JSON.stringify(clean8));
87860
+ }
87861
+ function loadCapabilityCopy(storage2) {
87862
+ try {
87863
+ const v = JSON.parse(storage2.getItem(K_CAPS) || "[]");
87864
+ return Array.isArray(v) ? v : [];
87865
+ } catch {
87866
+ return [];
87867
+ }
87868
+ }
87869
+ function pendingQueue(storage2) {
87870
+ try {
87871
+ const v = JSON.parse(storage2.getItem(K_QUEUE) || "[]");
87872
+ return Array.isArray(v) ? v : [];
87873
+ } catch {
87874
+ return [];
87875
+ }
87876
+ }
87877
+ function setQueue(storage2, q) {
87878
+ storage2.setItem(K_QUEUE, JSON.stringify(q.slice(-20)));
87879
+ }
87880
+ async function flushQueuedGrants(base6, storage2, opts = {}) {
87881
+ const f = opts.fetchImpl || fetch;
87882
+ const q = pendingQueue(storage2);
87883
+ const keep = [];
87884
+ const results = [];
87885
+ let sent = 0, failed = 0;
87886
+ for (const item of q) {
87887
+ try {
87888
+ const r = await post(base6, "/api/contacts/grants/sync", { grant: { ...item.grant, signature: item.signature } }, f);
87889
+ if (r.status === 200 && r.json?.ok) {
87890
+ sent++;
87891
+ results.push({ grantId: item.grant.grantId, ok: true });
87892
+ continue;
87893
+ }
87894
+ failed++;
87895
+ keep.push(item);
87896
+ results.push({ grantId: item.grant.grantId, ok: false, error: r.json?.code || r.status });
87897
+ } catch {
87898
+ failed++;
87899
+ keep.push(item);
87900
+ results.push({ grantId: item.grant.grantId, ok: false, error: "network" });
87901
+ }
87902
+ }
87903
+ setQueue(storage2, keep);
87904
+ return { sent, failed, results };
87905
+ }
87906
+ async function authorizeFromPhone(input) {
87907
+ const c = cryptoOf(input.cryptoObj);
87908
+ if (!ed25519Available(c || void 0)) {
87909
+ return { ok: false, error: "device_signing_unavailable: \u672C\u673A WebView \u4E0D\u652F\u6301 Ed25519 \u8BBE\u5907\u7B7E\u540D \u2014\u2014 \u8BF7\u5728\u684C\u9762\u7AEF\u6388\u6743 (\u6216\u5347\u7EA7\u7CFB\u7EDF WebView)" };
87910
+ }
87911
+ const dev = await loadOrCreateDeviceKey(input.storage, c);
87912
+ const preset = presetForChoice(input.choice);
87913
+ const grant = {
87914
+ grantId: `gr-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`,
87915
+ identityId: input.identityId || `sid-phone-${dev.deviceId.slice(-6)}`,
87916
+ ownerDid: input.ownerDid,
87917
+ ...preset,
87918
+ grantedAt: (/* @__PURE__ */ new Date()).toISOString(),
87919
+ grantedBy: input.grantedBy || "leo",
87920
+ grantedVia: "mobile",
87921
+ deviceIds: [dev.deviceId],
87922
+ status: "active",
87923
+ grantVersion: 1
87924
+ };
87925
+ const signature = await signGrantOnDevice(grant, input.storage, c);
87926
+ const f = input.fetchImpl || fetch;
87927
+ const reg = await registerDeviceOnDesktop(input.base, input.storage, { fetchImpl: f, cryptoObj: c }).catch(() => ({ ok: false }));
87928
+ if (!reg.ok) {
87929
+ const q2 = pendingQueue(input.storage);
87930
+ q2.push({ grant, signature, queuedAt: (/* @__PURE__ */ new Date()).toISOString() });
87931
+ setQueue(input.storage, q2);
87932
+ return {
87933
+ ok: false,
87934
+ queued: true,
87935
+ error: `\u684C\u9762\u6682\u4E0D\u53EF\u8FBE \u2192 \u6388\u6743\u5DF2\u5B58\u5728\u624B\u673A, \u5F85\u684C\u9762\u5728\u7EBF\u65F6\u81EA\u52A8\u540C\u6B65 (${reg.error || "offline"})`,
87936
+ data: { grantId: grant.grantId, level: grant.level, signature },
87937
+ note: "\u684C\u9762\u4E0D\u5728\u7EBF\u65F6\u4E0D\u5047\u88C5\u5DF2\u751F\u6548: \u6388\u6743\u5728\u672C\u5730\u961F\u5217\u91CC, \u684C\u9762\u4E00\u4E0A\u7EBF\u5C31\u8865\u540C\u6B65"
87938
+ };
87939
+ }
87940
+ const r = await post(input.base, "/api/contacts/grants/sync", { grant: { ...grant, signature } }, f);
87941
+ if (r.status === 200 && r.json?.ok) {
87942
+ return { ok: true, data: { grantId: grant.grantId, level: grant.level, signature }, note: "\u684C\u9762\u5DF2\u9A8C\u7B7E\u63A5\u53D7" };
87943
+ }
87944
+ const q = pendingQueue(input.storage);
87945
+ q.push({ grant, signature, queuedAt: (/* @__PURE__ */ new Date()).toISOString() });
87946
+ setQueue(input.storage, q);
87947
+ return {
87948
+ ok: false,
87949
+ queued: true,
87950
+ error: `\u684C\u9762\u62D2\u7EDD/\u672A\u63A5\u53D7: ${r.json?.code || r.json?.reason || r.status}`,
87951
+ data: { grantId: grant.grantId, level: grant.level, signature },
87952
+ note: "\u5DF2\u4FDD\u5B58\u5728\u624B\u673A\u672C\u5730\u961F\u5217 (\u4E0D\u4F1A\u56E0\u4E3A\u5931\u8D25\u5C31\u5F53\u6210\u5DF2\u6388\u6743)"
87953
+ };
87954
+ }
87955
+ async function revokeFromPhone(opts) {
87956
+ const c = cryptoOf(opts.cryptoObj);
87957
+ if (!ed25519Available(c || void 0)) return { ok: false, error: "device_signing_unavailable: \u672C\u673A\u4E0D\u652F\u6301\u8BBE\u5907\u7B7E\u540D, \u64A4\u9500\u9700\u5728\u684C\u9762\u7AEF\u8FDB\u884C" };
87958
+ const revokedAt = (/* @__PURE__ */ new Date()).toISOString();
87959
+ const payload = { grantId: opts.grantId, grantVersion: opts.version || 1, revokedAt, by: "mobile" };
87960
+ const signature = await signRevocationOnDevice(payload, opts.storage, c);
87961
+ const f = opts.fetchImpl || fetch;
87962
+ const r = await post(opts.base, "/api/contacts/grants/revoke-sync", { grantId: opts.grantId, by: "mobile", reason: opts.reason, version: payload.grantVersion, revokedAt, signature }, f);
87963
+ if (r.status === 200 && r.json?.ok) return { ok: true, data: { grantId: opts.grantId }, note: "\u684C\u9762\u5DF2\u9A8C\u7B7E\u5E76\u7ACB\u5373\u5931\u6548" };
87964
+ return { ok: false, error: `\u64A4\u9500\u672A\u9001\u8FBE: ${r.json?.code || r.json?.error || r.status}`, note: "\u684C\u9762\u4E0D\u5728\u7EBF\u65F6\u64A4\u9500**\u4E0D\u4F1A**\u88AB\u5F53\u4F5C\u5DF2\u5B8C\u6210 \u2014\u2014 \u8BF7\u5728\u684C\u9762\u5728\u7EBF\u540E\u91CD\u8BD5 (\u6216\u684C\u9762\u7AEF\u64A4\u9500)" };
87965
+ }
87966
+ async function loadFromDesktop(base6, storage2, opts = {}) {
87967
+ const f = opts.fetchImpl || fetch;
87968
+ if (!base6) {
87969
+ const caps = loadCapabilityCopy(storage2);
87970
+ return {
87971
+ ok: false,
87972
+ error: "desktop_offline",
87973
+ data: { contacts: caps, grants: [], effective: "\u684C\u9762\u4E0D\u5728\u7EBF (\u663E\u793A\u624B\u673A\u672C\u5730\u526F\u672C)", approvals: [], waiting: [] },
87974
+ note: "\u4EE5\u4E0B\u662F\u4F60\u624B\u673A\u4E0A\u4FDD\u5B58\u7684\u80FD\u529B\u526F\u672C (\u4E0D\u662F\u684C\u9762\u5B9E\u65F6\u72B6\u6001)"
87975
+ };
87976
+ }
87977
+ try {
87978
+ const a = await get(base6, "/api/contacts", f);
87979
+ const b = await get(base6, "/api/contacts/grants", f);
87980
+ if (a.status !== 200 && b.status !== 200) return { ok: false, error: `desktop_http_${a.status || b.status}` };
87981
+ const view = {
87982
+ contacts: a.json?.contacts || [],
87983
+ grants: b.json?.grants || [],
87984
+ effective: b.json?.effective || "",
87985
+ approvals: a.json?.approvals || [],
87986
+ waiting: a.json?.waiting || []
87987
+ };
87988
+ saveCapabilityCopy(storage2, (view.contacts || []).filter((c) => c.capabilities?.includes("send")).map((c) => ({
87989
+ contactId: c.contactId,
87990
+ kind: c.kind,
87991
+ displayValue: c.displayValue,
87992
+ verificationStatus: c.status,
87993
+ capabilities: c.capabilities,
87994
+ provider: c.provider,
87995
+ state: c.status
87996
+ })));
87997
+ return { ok: true, data: view };
87998
+ } catch (err2) {
87999
+ const caps = loadCapabilityCopy(storage2);
88000
+ return {
88001
+ ok: false,
88002
+ error: "desktop_offline",
88003
+ data: { contacts: caps, grants: [], effective: "\u684C\u9762\u4E0D\u5728\u7EBF (\u663E\u793A\u624B\u673A\u672C\u5730\u526F\u672C)", approvals: [], waiting: [] },
88004
+ note: `\u8FDE\u4E0D\u4E0A\u684C\u9762 (${String(err2?.message || err2).slice(0, 60)}) \u2014\u2014 \u663E\u793A\u672C\u5730\u526F\u672C`
88005
+ };
88006
+ }
88007
+ }
88008
+ async function bindFromPhone(base6, opts, storage2, f) {
88009
+ const impl = f || fetch;
88010
+ if (!base6) return { ok: false, error: "desktop_offline: \u7ED1\u5B9A\u9700\u8981\u684C\u9762\u7AEF\u5728\u7EBF (\u684C\u9762\u8D1F\u8D23\u843D\u76D8\u4E0E\u9A8C\u8BC1\u7801\u901A\u9053)" };
88011
+ const r = await post(base6, "/api/contacts/bind", { kind: opts.kind, value: opts.value, region: opts.region }, impl);
88012
+ if (r.status === 200 && r.json?.ok) return { ok: true, data: r.json };
88013
+ return { ok: false, error: r.json?.error || `HTTP ${r.status}` };
88014
+ }
88015
+ async function verifyFromPhone(base6, opts, storage2, f) {
88016
+ const impl = f || fetch;
88017
+ if (!base6) return { ok: false, error: "desktop_offline: \u9A8C\u8BC1\u9700\u8981\u684C\u9762\u7AEF\u5728\u7EBF" };
88018
+ const r = await post(base6, "/api/contacts/verify", { contactId: opts.contactId, challengeId: opts.challengeId, code: opts.code }, impl);
88019
+ if (r.status === 200 && r.json?.ok) return { ok: true, data: r.json };
88020
+ return { ok: false, error: r.json?.error || `HTTP ${r.status}` };
88021
+ }
88022
+ async function decideApprovalFromPhone(base6, opts, f) {
88023
+ const impl = f || fetch;
88024
+ if (!base6) return { ok: false, error: "desktop_offline: \u6279\u51C6\u9700\u8981\u684C\u9762\u7AEF\u5728\u7EBF (\u624B\u673A\u4E0D\u5728\u7EBF\u65F6\u4E0D\u4F1A\u5047\u88C5\u5DF2\u6279\u51C6)" };
88025
+ const r = await post(base6, `/api/contacts/approvals/${encodeURIComponent(opts.consentId)}/${opts.action}`, { by: "leo", via: "mobile", body: opts.body, reason: opts.reason }, impl);
88026
+ if (r.status === 200 && r.json?.ok) return { ok: true, data: r.json };
88027
+ return { ok: false, error: r.json?.error || `HTTP ${r.status}` };
88028
+ }
88029
+ function buildPhoneCard(view, opts) {
88030
+ return {
88031
+ title: "\u8BA9 Bolloon \u4EE3\u8868\u4F60\u8054\u7CFB\u5916\u90E8\u7684\u4EBA",
88032
+ effective: view?.effective || opts.offline || "\u672A\u77E5",
88033
+ contacts: (view?.contacts || []).map((c) => ({
88034
+ displayValue: c.displayValue || "",
88035
+ kind: c.kind || "",
88036
+ state: c.status || c.verificationStatus || "",
88037
+ label: c.capabilities?.includes("send") ? "\u53EF\u8054\u7CFB" : "\u4E0D\u53EF\u8054\u7CFB"
88038
+ })),
88039
+ approvals: (view?.approvals || []).map((a) => ({
88040
+ consentId: a.consentId,
88041
+ contactName: a.contactName,
88042
+ channel: a.channel,
88043
+ subject: a.subject,
88044
+ bodyPreview: a.bodyPreview || "",
88045
+ reason: a.reason,
88046
+ reallySent: !!a.reallySent
88047
+ })),
88048
+ choices: [
88049
+ { id: "task_once", label: "\u4EC5\u672C\u6B21\u4EFB\u52A1" },
88050
+ { id: "persistent", label: "\u957F\u671F\u4F7F\u7528 (\u63A8\u8350)" },
88051
+ { id: "full_contact_access", label: "\u5B8C\u5168\u6388\u6743\u8054\u7CFB\u65B9\u5F0F\u80FD\u529B" }
88052
+ ],
88053
+ willNotGet: ["\u8BFB\u53D6\u5168\u90E8\u90AE\u7BB1", "\u8BFB\u53D6\u901A\u8BAF\u5F55", "\u7FA4\u53D1\u6D88\u606F", "\u81EA\u52A8\u652F\u4ED8/\u8F6C\u8D26", "\u4EE3\u8868\u4F60\u7B7E\u7F72\u5408\u540C", "\u62FF\u5230\u624B\u673A\u53F7/\u90AE\u7BB1\u660E\u6587"],
88054
+ deviceSigning: opts.deviceSigning
88055
+ };
88056
+ }
88057
+ var K_DEVICE, K_QUEUE, K_CAPS, cryptoOf;
88058
+ var init_mobile_contacts = __esm({
88059
+ "src/web/mobile-contacts.ts"() {
88060
+ "use strict";
88061
+ init_grant_payload();
88062
+ K_DEVICE = "bolloon.contacts.device.v1";
88063
+ K_QUEUE = "bolloon.contacts.queue.v1";
88064
+ K_CAPS = "bolloon.contacts.capabilities.v1";
88065
+ cryptoOf = (c) => c || globalThis.crypto || null;
88066
+ }
88067
+ });
88068
+
87669
88069
  // src/web/mobile-social.ts
87670
88070
  var mobile_social_exports = {};
87671
88071
  __export(mobile_social_exports, {
@@ -90103,13 +90503,13 @@ ${r.steps.map((s3) => `${s3.ok ? "\u2713" : "\u2717"} ${s3.step}: ${s3.note}`).j
90103
90503
  const windows = Math.ceil(scalarBits2 / W2) + 1;
90104
90504
  const windowSize = 2 ** (W2 - 1);
90105
90505
  const maxNumber = 2 ** W2;
90106
- const mask = bitMask3(W2);
90506
+ const mask2 = bitMask3(W2);
90107
90507
  const shiftBy = BigInt(W2);
90108
- return { windows, windowSize, mask, maxNumber, shiftBy };
90508
+ return { windows, windowSize, mask: mask2, maxNumber, shiftBy };
90109
90509
  }
90110
90510
  function calcOffsets3(n, window2, wOpts) {
90111
- const { windowSize, mask, maxNumber, shiftBy } = wOpts;
90112
- let wbits = Number(n & mask);
90511
+ const { windowSize, mask: mask2, maxNumber, shiftBy } = wOpts;
90512
+ let wbits = Number(n & mask2);
90113
90513
  let nextN = n >> shiftBy;
90114
90514
  if (wbits > windowSize) {
90115
90515
  wbits -= maxNumber;
@@ -95154,10 +95554,10 @@ ${r.steps.map((s3) => `${s3.ok ? "\u2713" : "\u2717"} ${s3.step}: ${s3.note}`).j
95154
95554
  let f = G;
95155
95555
  const pow_2_w = 2 ** W;
95156
95556
  const maxNum = pow_2_w;
95157
- const mask = big(pow_2_w - 1);
95557
+ const mask2 = big(pow_2_w - 1);
95158
95558
  const shiftBy = big(W);
95159
95559
  for (let w2 = 0; w2 < pwindows; w2++) {
95160
- let wbits = Number(n & mask);
95560
+ let wbits = Number(n & mask2);
95161
95561
  n >>= shiftBy;
95162
95562
  if (wbits > pwindowSize) {
95163
95563
  wbits -= maxNum;
@@ -96895,11 +97295,11 @@ ${r.steps.map((s3) => `${s3.ok ? "\u2713" : "\u2717"} ${s3.step}: ${s3.note}`).j
96895
97295
  function toToken6(data, pos, prefix, length20, options2) {
96896
97296
  const totLength = prefix + length20;
96897
97297
  assertEnoughData2(data, pos, totLength);
96898
- const tok = new Token2(Type2.string, toStr2(data, pos + prefix, pos + totLength), totLength);
97298
+ const tok2 = new Token2(Type2.string, toStr2(data, pos + prefix, pos + totLength), totLength);
96899
97299
  if (options2.retainStringBytes === true) {
96900
- tok.byteValue = data.slice(pos + prefix, pos + totLength);
97300
+ tok2.byteValue = data.slice(pos + prefix, pos + totLength);
96901
97301
  }
96902
- return tok;
97302
+ return tok2;
96903
97303
  }
96904
97304
  function decodeStringCompact2(data, pos, minor, options2) {
96905
97305
  return toToken6(data, pos, 1, minor, options2);
@@ -98382,7 +98782,7 @@ ${r.steps.map((s3) => `${s3.ok ? "\u2713" : "\u2717"} ${s3.step}: ${s3.note}`).j
98382
98782
  }
98383
98783
  function encode15(data, alphabet4, bitsPerChar) {
98384
98784
  const pad2 = alphabet4[alphabet4.length - 1] === "=";
98385
- const mask = (1 << bitsPerChar) - 1;
98785
+ const mask2 = (1 << bitsPerChar) - 1;
98386
98786
  let out = "";
98387
98787
  let bits = 0;
98388
98788
  let buffer6 = 0;
@@ -98391,11 +98791,11 @@ ${r.steps.map((s3) => `${s3.ok ? "\u2713" : "\u2717"} ${s3.step}: ${s3.note}`).j
98391
98791
  bits += 8;
98392
98792
  while (bits > bitsPerChar) {
98393
98793
  bits -= bitsPerChar;
98394
- out += alphabet4[mask & buffer6 >> bits];
98794
+ out += alphabet4[mask2 & buffer6 >> bits];
98395
98795
  }
98396
98796
  }
98397
98797
  if (bits !== 0) {
98398
- out += alphabet4[mask & buffer6 << bitsPerChar - bits];
98798
+ out += alphabet4[mask2 & buffer6 << bitsPerChar - bits];
98399
98799
  }
98400
98800
  if (pad2) {
98401
98801
  while ((out.length * bitsPerChar & 7) !== 0) {
@@ -99216,7 +99616,7 @@ ${r.steps.map((s3) => `${s3.ok ? "\u2713" : "\u2717"} ${s3.step}: ${s3.note}`).j
99216
99616
  computeCid: () => computeCid,
99217
99617
  createIpfsClient: () => createIpfsClient,
99218
99618
  createMemoryStorage: () => createMemoryStorage,
99219
- defaultStorage: () => defaultStorage2,
99619
+ defaultStorage: () => defaultStorage3,
99220
99620
  getIpfsConfig: () => getIpfsConfig,
99221
99621
  ipfsCacheGet: () => ipfsCacheGet,
99222
99622
  ipfsCacheSet: () => ipfsCacheSet,
@@ -99247,7 +99647,7 @@ ${r.steps.map((s3) => `${s3.ok ? "\u2713" : "\u2717"} ${s3.step}: ${s3.note}`).j
99247
99647
  }
99248
99648
  };
99249
99649
  }
99250
- function defaultStorage2() {
99650
+ function defaultStorage3() {
99251
99651
  try {
99252
99652
  if (typeof localStorage !== "undefined" && localStorage) {
99253
99653
  const probe = "__bolloon_ipfs_probe__";
@@ -99317,7 +99717,7 @@ ${r.steps.map((s3) => `${s3.ok ? "\u2713" : "\u2717"} ${s3.step}: ${s3.note}`).j
99317
99717
  }
99318
99718
  }
99319
99719
  }
99320
- function getIpfsConfig(storage2 = defaultStorage2()) {
99720
+ function getIpfsConfig(storage2 = defaultStorage3()) {
99321
99721
  const base6 = { mode: "public", gateways: [...DEFAULT_GATEWAYS] };
99322
99722
  try {
99323
99723
  const raw = storage2.getItem(IPFS_CONFIG_KEY);
@@ -99336,7 +99736,7 @@ ${r.steps.map((s3) => `${s3.ok ? "\u2713" : "\u2717"} ${s3.step}: ${s3.note}`).j
99336
99736
  return base6;
99337
99737
  }
99338
99738
  }
99339
- function setIpfsConfig(cfg, storage2 = defaultStorage2()) {
99739
+ function setIpfsConfig(cfg, storage2 = defaultStorage3()) {
99340
99740
  try {
99341
99741
  if (cfg.mode !== void 0 && !VALID_MODES.includes(cfg.mode)) {
99342
99742
  return { ok: false, error: `\u975E\u6CD5 mode: ${String(cfg.mode)} (\u5E94\u4E3A public|remote|pinata)` };
@@ -99384,7 +99784,7 @@ ${r.steps.map((s3) => `${s3.ok ? "\u2713" : "\u2717"} ${s3.step}: ${s3.note}`).j
99384
99784
  }
99385
99785
  return kept;
99386
99786
  }
99387
- function ipfsCacheGet(cid, storage2 = defaultStorage2()) {
99787
+ function ipfsCacheGet(cid, storage2 = defaultStorage3()) {
99388
99788
  try {
99389
99789
  const key = normalizeCid(cid);
99390
99790
  if (!key) return null;
@@ -99404,7 +99804,7 @@ ${r.steps.map((s3) => `${s3.ok ? "\u2713" : "\u2717"} ${s3.step}: ${s3.note}`).j
99404
99804
  return null;
99405
99805
  }
99406
99806
  }
99407
- function ipfsCacheSet(cid, text, storage2 = defaultStorage2()) {
99807
+ function ipfsCacheSet(cid, text, storage2 = defaultStorage3()) {
99408
99808
  try {
99409
99809
  const key = normalizeCid(cid);
99410
99810
  if (!key) return { ok: false, error: "cid \u4E0D\u80FD\u4E3A\u7A7A" };
@@ -99488,7 +99888,7 @@ ${r.steps.map((s3) => `${s3.ok ? "\u2713" : "\u2717"} ${s3.step}: ${s3.note}`).j
99488
99888
  async function ipfsFetch(cid, opts = {}) {
99489
99889
  const key = normalizeCid(cid);
99490
99890
  if (!key) return { ok: false, error: "cid \u4E0D\u80FD\u4E3A\u7A7A" };
99491
- const storage2 = opts.storage ?? defaultStorage2();
99891
+ const storage2 = opts.storage ?? defaultStorage3();
99492
99892
  const useCache = opts.useCache !== false;
99493
99893
  if (useCache) {
99494
99894
  const cached = ipfsCacheGet(key, storage2);
@@ -100794,8 +101194,8 @@ ${r.steps.map((s3) => `${s3.ok ? "\u2713" : "\u2717"} ${s3.step}: ${s3.note}`).j
100794
101194
  }
100795
101195
  function encodeInteger2(value2) {
100796
101196
  const contents = new Uint8ArrayList10();
100797
- const mask = 128;
100798
- const positive2 = (value2.subarray()[0] & mask) === mask;
101197
+ const mask2 = 128;
101198
+ const positive2 = (value2.subarray()[0] & mask2) === mask2;
100799
101199
  if (positive2) {
100800
101200
  contents.append(Uint8Array.from([0]));
100801
101201
  }
@@ -100952,18 +101352,18 @@ ${r.steps.map((s3) => `${s3.ok ? "\u2713" : "\u2717"} ${s3.step}: ${s3.note}`).j
100952
101352
  * Zig-zag encodes this long bits
100953
101353
  */
100954
101354
  zzEncode() {
100955
- const mask = this.hi >> 31;
100956
- this.hi = ((this.hi << 1 | this.lo >>> 31) ^ mask) >>> 0;
100957
- this.lo = (this.lo << 1 ^ mask) >>> 0;
101355
+ const mask2 = this.hi >> 31;
101356
+ this.hi = ((this.hi << 1 | this.lo >>> 31) ^ mask2) >>> 0;
101357
+ this.lo = (this.lo << 1 ^ mask2) >>> 0;
100958
101358
  return this;
100959
101359
  }
100960
101360
  /**
100961
101361
  * Zig-zag decodes this long bits
100962
101362
  */
100963
101363
  zzDecode() {
100964
- const mask = -(this.lo & 1);
100965
- this.lo = ((this.lo >>> 1 | this.hi << 31) ^ mask) >>> 0;
100966
- this.hi = (this.hi >>> 1 ^ mask) >>> 0;
101364
+ const mask2 = -(this.lo & 1);
101365
+ this.lo = ((this.lo >>> 1 | this.hi << 31) ^ mask2) >>> 0;
101366
+ this.hi = (this.hi >>> 1 ^ mask2) >>> 0;
100967
101367
  return this;
100968
101368
  }
100969
101369
  /**
@@ -103864,18 +104264,18 @@ ${r.steps.map((s3) => `${s3.ok ? "\u2713" : "\u2717"} ${s3.step}: ${s3.note}`).j
103864
104264
  * Zig-zag encodes this long bits
103865
104265
  */
103866
104266
  zzEncode() {
103867
- const mask = this.hi >> 31;
103868
- this.hi = ((this.hi << 1 | this.lo >>> 31) ^ mask) >>> 0;
103869
- this.lo = (this.lo << 1 ^ mask) >>> 0;
104267
+ const mask2 = this.hi >> 31;
104268
+ this.hi = ((this.hi << 1 | this.lo >>> 31) ^ mask2) >>> 0;
104269
+ this.lo = (this.lo << 1 ^ mask2) >>> 0;
103870
104270
  return this;
103871
104271
  }
103872
104272
  /**
103873
104273
  * Zig-zag decodes this long bits
103874
104274
  */
103875
104275
  zzDecode() {
103876
- const mask = -(this.lo & 1);
103877
- this.lo = ((this.lo >>> 1 | this.hi << 31) ^ mask) >>> 0;
103878
- this.hi = (this.hi >>> 1 ^ mask) >>> 0;
104276
+ const mask2 = -(this.lo & 1);
104277
+ this.lo = ((this.lo >>> 1 | this.hi << 31) ^ mask2) >>> 0;
104278
+ this.hi = (this.hi >>> 1 ^ mask2) >>> 0;
103879
104279
  return this;
103880
104280
  }
103881
104281
  /**
@@ -114756,18 +115156,18 @@ ${values.join("\n")}` : `${blockName} :`;
114756
115156
  * Zig-zag encodes this long bits
114757
115157
  */
114758
115158
  zzEncode() {
114759
- const mask = this.hi >> 31;
114760
- this.hi = ((this.hi << 1 | this.lo >>> 31) ^ mask) >>> 0;
114761
- this.lo = (this.lo << 1 ^ mask) >>> 0;
115159
+ const mask2 = this.hi >> 31;
115160
+ this.hi = ((this.hi << 1 | this.lo >>> 31) ^ mask2) >>> 0;
115161
+ this.lo = (this.lo << 1 ^ mask2) >>> 0;
114762
115162
  return this;
114763
115163
  }
114764
115164
  /**
114765
115165
  * Zig-zag decodes this long bits
114766
115166
  */
114767
115167
  zzDecode() {
114768
- const mask = -(this.lo & 1);
114769
- this.lo = ((this.lo >>> 1 | this.hi << 31) ^ mask) >>> 0;
114770
- this.hi = (this.hi >>> 1 ^ mask) >>> 0;
115168
+ const mask2 = -(this.lo & 1);
115169
+ this.lo = ((this.lo >>> 1 | this.hi << 31) ^ mask2) >>> 0;
115170
+ this.hi = (this.hi >>> 1 ^ mask2) >>> 0;
114771
115171
  return this;
114772
115172
  }
114773
115173
  /**
@@ -117096,18 +117496,18 @@ ${values.join("\n")}` : `${blockName} :`;
117096
117496
  * Zig-zag encodes this long bits
117097
117497
  */
117098
117498
  zzEncode() {
117099
- const mask = this.hi >> 31;
117100
- this.hi = ((this.hi << 1 | this.lo >>> 31) ^ mask) >>> 0;
117101
- this.lo = (this.lo << 1 ^ mask) >>> 0;
117499
+ const mask2 = this.hi >> 31;
117500
+ this.hi = ((this.hi << 1 | this.lo >>> 31) ^ mask2) >>> 0;
117501
+ this.lo = (this.lo << 1 ^ mask2) >>> 0;
117102
117502
  return this;
117103
117503
  }
117104
117504
  /**
117105
117505
  * Zig-zag decodes this long bits
117106
117506
  */
117107
117507
  zzDecode() {
117108
- const mask = -(this.lo & 1);
117109
- this.lo = ((this.lo >>> 1 | this.hi << 31) ^ mask) >>> 0;
117110
- this.hi = (this.hi >>> 1 ^ mask) >>> 0;
117508
+ const mask2 = -(this.lo & 1);
117509
+ this.lo = ((this.lo >>> 1 | this.hi << 31) ^ mask2) >>> 0;
117510
+ this.hi = (this.hi >>> 1 ^ mask2) >>> 0;
117111
117511
  return this;
117112
117512
  }
117113
117513
  /**
@@ -129350,12 +129750,12 @@ ${values.join("\n")}` : `${blockName} :`;
129350
129750
  type: MESSAGE_TYPES[opcode]
129351
129751
  };
129352
129752
  }
129353
- let mask;
129753
+ let mask2;
129354
129754
  if (isMasked) {
129355
129755
  if (buf.byteLength < offset + 4) {
129356
129756
  return;
129357
129757
  }
129358
- mask = buf.subarray(offset, offset + 4);
129758
+ mask2 = buf.subarray(offset, offset + 4);
129359
129759
  offset += 4;
129360
129760
  }
129361
129761
  if (buf.byteLength < offset + length20) {
@@ -129363,8 +129763,8 @@ ${values.join("\n")}` : `${blockName} :`;
129363
129763
  }
129364
129764
  let data = buf.subarray(offset, offset + length20);
129365
129765
  offset += length20;
129366
- if (mask != null) {
129367
- data = applyMask(data, mask);
129766
+ if (mask2 != null) {
129767
+ data = applyMask(data, mask2);
129368
129768
  }
129369
129769
  buf.consume(offset);
129370
129770
  return {
@@ -129372,12 +129772,12 @@ ${values.join("\n")}` : `${blockName} :`;
129372
129772
  data
129373
129773
  };
129374
129774
  }
129375
- function applyMask(data, mask) {
129775
+ function applyMask(data, mask2) {
129376
129776
  let m2 = 0;
129377
129777
  for (let i = 0; i < data.byteLength; i++) {
129378
- data[i] = data[i] ^ mask[m2];
129778
+ data[i] = data[i] ^ mask2[m2];
129379
129779
  m2++;
129380
- if (m2 === mask.byteLength) {
129780
+ if (m2 === mask2.byteLength) {
129381
129781
  m2 = 0;
129382
129782
  }
129383
129783
  }
@@ -131917,18 +132317,18 @@ ${values.join("\n")}` : `${blockName} :`;
131917
132317
  * Zig-zag encodes this long bits
131918
132318
  */
131919
132319
  zzEncode() {
131920
- const mask = this.hi >> 31;
131921
- this.hi = ((this.hi << 1 | this.lo >>> 31) ^ mask) >>> 0;
131922
- this.lo = (this.lo << 1 ^ mask) >>> 0;
132320
+ const mask2 = this.hi >> 31;
132321
+ this.hi = ((this.hi << 1 | this.lo >>> 31) ^ mask2) >>> 0;
132322
+ this.lo = (this.lo << 1 ^ mask2) >>> 0;
131923
132323
  return this;
131924
132324
  }
131925
132325
  /**
131926
132326
  * Zig-zag decodes this long bits
131927
132327
  */
131928
132328
  zzDecode() {
131929
- const mask = -(this.lo & 1);
131930
- this.lo = ((this.lo >>> 1 | this.hi << 31) ^ mask) >>> 0;
131931
- this.hi = (this.hi >>> 1 ^ mask) >>> 0;
132329
+ const mask2 = -(this.lo & 1);
132330
+ this.lo = ((this.lo >>> 1 | this.hi << 31) ^ mask2) >>> 0;
132331
+ this.hi = (this.hi >>> 1 ^ mask2) >>> 0;
131932
132332
  return this;
131933
132333
  }
131934
132334
  /**
@@ -135418,12 +135818,12 @@ ${values.join("\n")}` : `${blockName} :`;
135418
135818
  g[i] &= 8191;
135419
135819
  }
135420
135820
  g[9] -= 1 << 13;
135421
- let mask = (c ^ 1) - 1;
135821
+ let mask2 = (c ^ 1) - 1;
135422
135822
  for (let i = 0; i < 10; i++)
135423
- g[i] &= mask;
135424
- mask = ~mask;
135823
+ g[i] &= mask2;
135824
+ mask2 = ~mask2;
135425
135825
  for (let i = 0; i < 10; i++)
135426
- h3[i] = h3[i] & mask | g[i];
135826
+ h3[i] = h3[i] & mask2 | g[i];
135427
135827
  h3[0] = (h3[0] | h3[1] << 13) & 65535;
135428
135828
  h3[1] = (h3[1] >>> 3 | h3[2] << 10) & 65535;
135429
135829
  h3[2] = (h3[2] >>> 6 | h3[3] << 7) & 65535;
@@ -136870,18 +137270,18 @@ ${values.join("\n")}` : `${blockName} :`;
136870
137270
  * Zig-zag encodes this long bits
136871
137271
  */
136872
137272
  zzEncode() {
136873
- const mask = this.hi >> 31;
136874
- this.hi = ((this.hi << 1 | this.lo >>> 31) ^ mask) >>> 0;
136875
- this.lo = (this.lo << 1 ^ mask) >>> 0;
137273
+ const mask2 = this.hi >> 31;
137274
+ this.hi = ((this.hi << 1 | this.lo >>> 31) ^ mask2) >>> 0;
137275
+ this.lo = (this.lo << 1 ^ mask2) >>> 0;
136876
137276
  return this;
136877
137277
  }
136878
137278
  /**
136879
137279
  * Zig-zag decodes this long bits
136880
137280
  */
136881
137281
  zzDecode() {
136882
- const mask = -(this.lo & 1);
136883
- this.lo = ((this.lo >>> 1 | this.hi << 31) ^ mask) >>> 0;
136884
- this.hi = (this.hi >>> 1 ^ mask) >>> 0;
137282
+ const mask2 = -(this.lo & 1);
137283
+ this.lo = ((this.lo >>> 1 | this.hi << 31) ^ mask2) >>> 0;
137284
+ this.hi = (this.hi >>> 1 ^ mask2) >>> 0;
136885
137285
  return this;
136886
137286
  }
136887
137287
  /**
@@ -139954,11 +140354,11 @@ a=end-of-candidates
139954
140354
  function toToken10(data, pos, prefix, length20, options2) {
139955
140355
  const totLength = prefix + length20;
139956
140356
  assertEnoughData3(data, pos, totLength);
139957
- const tok = new Token3(Type3.string, toStr3(data, pos + prefix, pos + totLength), totLength);
140357
+ const tok2 = new Token3(Type3.string, toStr3(data, pos + prefix, pos + totLength), totLength);
139958
140358
  if (options2.retainStringBytes === true) {
139959
- tok.byteValue = data.slice(pos + prefix, pos + totLength);
140359
+ tok2.byteValue = data.slice(pos + prefix, pos + totLength);
139960
140360
  }
139961
- return tok;
140361
+ return tok2;
139962
140362
  }
139963
140363
  function decodeStringCompact3(data, pos, minor, options2) {
139964
140364
  return toToken10(data, pos, 1, minor, options2);
@@ -142177,11 +142577,11 @@ a=end-of-candidates
142177
142577
  function toToken14(data, pos, prefix, length20, options2) {
142178
142578
  const totLength = prefix + length20;
142179
142579
  assertEnoughData4(data, pos, totLength);
142180
- const tok = new Token4(Type4.string, toStr4(data, pos + prefix, pos + totLength), totLength);
142580
+ const tok2 = new Token4(Type4.string, toStr4(data, pos + prefix, pos + totLength), totLength);
142181
142581
  if (options2.retainStringBytes === true) {
142182
- tok.byteValue = data.slice(pos + prefix, pos + totLength);
142582
+ tok2.byteValue = data.slice(pos + prefix, pos + totLength);
142183
142583
  }
142184
- return tok;
142584
+ return tok2;
142185
142585
  }
142186
142586
  function decodeStringCompact4(data, pos, minor, options2) {
142187
142587
  return toToken14(data, pos, 1, minor, options2);
@@ -148151,11 +148551,11 @@ ${indent}[Error list was empty]`;
148151
148551
  function toToken18(data, pos, prefix, length20, options2) {
148152
148552
  const totLength = prefix + length20;
148153
148553
  assertEnoughData5(data, pos, totLength);
148154
- const tok = new Token5(Type5.string, toStr5(data, pos + prefix, pos + totLength), totLength);
148554
+ const tok2 = new Token5(Type5.string, toStr5(data, pos + prefix, pos + totLength), totLength);
148155
148555
  if (options2.retainStringBytes === true) {
148156
- tok.byteValue = data.slice(pos + prefix, pos + totLength);
148556
+ tok2.byteValue = data.slice(pos + prefix, pos + totLength);
148157
148557
  }
148158
- return tok;
148558
+ return tok2;
148159
148559
  }
148160
148560
  function decodeStringCompact5(data, pos, minor, options2) {
148161
148561
  return toToken18(data, pos, 1, minor, options2);
@@ -150573,14 +150973,14 @@ ${error.stack}` : head;
150573
150973
  return out;
150574
150974
  }
150575
150975
  }
150576
- function heliaEnabled(storage2 = defaultStorage2()) {
150976
+ function heliaEnabled(storage2 = defaultStorage3()) {
150577
150977
  try {
150578
150978
  return storage2.getItem(HELIA_ENABLED_KEY) === "1";
150579
150979
  } catch {
150580
150980
  return false;
150581
150981
  }
150582
150982
  }
150583
- function setHeliaEnabled(enabled, storage2 = defaultStorage2()) {
150983
+ function setHeliaEnabled(enabled, storage2 = defaultStorage3()) {
150584
150984
  try {
150585
150985
  storage2.setItem(HELIA_ENABLED_KEY, enabled ? "1" : "0");
150586
150986
  return { ok: true, enabled: !!enabled };
@@ -150926,8 +151326,8 @@ ${error.stack}` : head;
150926
151326
  N3: 40,
150927
151327
  N4: 10
150928
151328
  };
150929
- exports.isValid = function isValid2(mask) {
150930
- return mask != null && mask !== "" && !isNaN(mask) && mask >= 0 && mask <= 7;
151329
+ exports.isValid = function isValid2(mask2) {
151330
+ return mask2 != null && mask2 !== "" && !isNaN(mask2) && mask2 >= 0 && mask2 <= 7;
150931
151331
  };
150932
151332
  exports.from = function from6(value2) {
150933
151333
  return exports.isValid(value2) ? parseInt(value2, 10) : void 0;
@@ -151731,8 +152131,8 @@ ${error.stack}` : head;
151731
152131
  var G15 = 1 << 10 | 1 << 8 | 1 << 5 | 1 << 4 | 1 << 2 | 1 << 1 | 1 << 0;
151732
152132
  var G15_MASK = 1 << 14 | 1 << 12 | 1 << 10 | 1 << 4 | 1 << 1;
151733
152133
  var G15_BCH = Utils.getBCHDigit(G15);
151734
- exports.getEncodedBits = function getEncodedBits(errorCorrectionLevel, mask) {
151735
- const data = errorCorrectionLevel.bit << 3 | mask;
152134
+ exports.getEncodedBits = function getEncodedBits(errorCorrectionLevel, mask2) {
152135
+ const data = errorCorrectionLevel.bit << 3 | mask2;
151736
152136
  let d2 = data << 10;
151737
152137
  while (Utils.getBCHDigit(d2) - G15_BCH >= 0) {
151738
152138
  d2 ^= G15 << Utils.getBCHDigit(d2) - G15_BCH;
@@ -152459,16 +152859,16 @@ ${error.stack}` : head;
152459
152859
  }
152460
152860
  let errorCorrectionLevel = ECLevel.M;
152461
152861
  let version4;
152462
- let mask;
152862
+ let mask2;
152463
152863
  if (typeof options2 !== "undefined") {
152464
152864
  errorCorrectionLevel = ECLevel.from(options2.errorCorrectionLevel, ECLevel.M);
152465
152865
  version4 = Version.from(options2.version);
152466
- mask = MaskPattern.from(options2.maskPattern);
152866
+ mask2 = MaskPattern.from(options2.maskPattern);
152467
152867
  if (options2.toSJISFunc) {
152468
152868
  Utils.setToSJISFunction(options2.toSJISFunc);
152469
152869
  }
152470
152870
  }
152471
- return createSymbol(data, version4, errorCorrectionLevel, mask);
152871
+ return createSymbol(data, version4, errorCorrectionLevel, mask2);
152472
152872
  };
152473
152873
  }
152474
152874
  });
@@ -153787,8 +154187,8 @@ ${error.stack}` : head;
153787
154187
  var bitsLeft = 8 - this.bitOffset;
153788
154188
  var toRead = numBits < bitsLeft ? numBits : bitsLeft;
153789
154189
  var bitsToNotRead = bitsLeft - toRead;
153790
- var mask = 255 >> 8 - toRead << bitsToNotRead;
153791
- result = (this.bytes[this.byteOffset] & mask) >> bitsToNotRead;
154190
+ var mask2 = 255 >> 8 - toRead << bitsToNotRead;
154191
+ result = (this.bytes[this.byteOffset] & mask2) >> bitsToNotRead;
153792
154192
  numBits -= toRead;
153793
154193
  this.bitOffset += toRead;
153794
154194
  if (this.bitOffset === 8) {
@@ -153804,8 +154204,8 @@ ${error.stack}` : head;
153804
154204
  }
153805
154205
  if (numBits > 0) {
153806
154206
  var bitsToNotRead = 8 - numBits;
153807
- var mask = 255 >> bitsToNotRead << bitsToNotRead;
153808
- result = result << numBits | (this.bytes[this.byteOffset] & mask) >> bitsToNotRead;
154207
+ var mask2 = 255 >> bitsToNotRead << bitsToNotRead;
154208
+ result = result << numBits | (this.bytes[this.byteOffset] & mask2) >> bitsToNotRead;
153809
154209
  this.bitOffset += numBits;
153810
154210
  }
153811
154211
  }
@@ -162923,133 +163323,336 @@ ${error.stack}` : head;
162923
163323
  return result;
162924
163324
  }
162925
163325
 
162926
- // src/agents/contacts/grant-payload.ts
162927
- function canonicalGrantPayload(g) {
163326
+ // src/web/mobile-core.ts
163327
+ init_mobile_contacts();
163328
+
163329
+ // src/agents/mobile-task-actions.ts
163330
+ var MOBILE_TASK_ACTION_KINDS = [
163331
+ "group_join",
163332
+ // 入群 (加入一个 OrbitDB 群 store)
163333
+ "group_leave",
163334
+ // 退群
163335
+ "group_create",
163336
+ // 建群 (会自动入群)
163337
+ "announce_publish",
163338
+ // 发布任务公告 (落盘公告 + 向注册表公告 + 记脉冲)
163339
+ "announce_to_group",
163340
+ // 把板上的公告推进群里 (CLI: bolloon task announce)
163341
+ "trail_post"
163342
+ // 过程留痕: 接单/交付/初筛/终审 (CLI: bolloon task post)
163343
+ ];
163344
+ var TASK_ACTION_TTL_MS = 10 * 6e4;
163345
+ var TASK_ACTION_DEFAULT_TTL_MS = 5 * 6e4;
163346
+ var TASK_ACTION_CLOCK_SKEW_MS = 2 * 6e4;
163347
+ function tok(v) {
163348
+ if (v === null || v === void 0) return "-";
163349
+ const s2 = String(v);
163350
+ if (!s2) return "-";
163351
+ return s2.replace(/\r?\n/g, "\\n");
163352
+ }
163353
+ function taskActionContentText(req) {
163354
+ const kind = String(req?.kind ?? "");
163355
+ const base6 = [
163356
+ `kind=${tok(kind)}`,
163357
+ `group=${tok(req?.groupRef)}`,
163358
+ `ann=${tok(req?.announcementId)}`
163359
+ ];
163360
+ if (kind === "announce_publish") {
163361
+ base6.push(`cap=${tok(req?.capability)}`);
163362
+ base6.push(`budget=${tok(req?.budgetHuman)}${req?.currency ? `@${tok(req.currency)}` : ""}`);
163363
+ base6.push(`deadline=${tok(req?.deadline)}`);
163364
+ base6.push(`instruction=${tok(req?.instruction)}`);
163365
+ } else if (kind === "announce_to_group") {
163366
+ base6.push(`criteria=${tok(req?.criteria)}`);
163367
+ } else if (kind === "trail_post") {
163368
+ base6.push(`trail=${tok(req?.trailKind)}`);
163369
+ base6.push(`round=${tok(req?.round)}`);
163370
+ base6.push(`price=${tok(req?.price)}`);
163371
+ base6.push(`hash=${tok(req?.hash)}`);
163372
+ base6.push(`bytes=${tok(req?.bytes)}`);
163373
+ base6.push(`checks=${tok(req?.checks)}`);
163374
+ base6.push(`verdict=${tok(req?.verdict)}`);
163375
+ }
163376
+ return base6.join("\n");
163377
+ }
163378
+ function canonicalTaskActionPayload(a) {
162928
163379
  return JSON.stringify({
162929
- grantId: g.grantId,
162930
- identityId: g.identityId,
162931
- ownerDid: g.ownerDid,
162932
- level: g.level,
162933
- channels: g.channels,
162934
- contactScope: g.contactScope,
162935
- taskScope: g.taskScope,
162936
- contentScope: g.contentScope,
162937
- replyWakeAllowed: g.replyWakeAllowed,
162938
- autoSend: g.autoSend,
162939
- sensitiveContentAllowed: g.sensitiveContentAllowed,
162940
- newRecipientAllowed: g.newRecipientAllowed,
162941
- grantedAt: g.grantedAt,
162942
- grantedBy: g.grantedBy,
162943
- grantedVia: g.grantedVia,
162944
- deviceIds: [...g.deviceIds].sort(),
162945
- status: g.status,
162946
- revokedAt: g.revokedAt || "",
162947
- grantVersion: g.grantVersion
163380
+ actionId: a.actionId,
163381
+ kind: a.kind,
163382
+ deviceId: a.deviceId,
163383
+ ownerDid: a.ownerDid,
163384
+ targetRef: a.targetRef || "",
163385
+ contentDigest: a.contentDigest,
163386
+ createdAt: a.createdAt,
163387
+ expiresAt: a.expiresAt,
163388
+ grantedBy: a.grantedBy,
163389
+ via: a.via
162948
163390
  });
162949
163391
  }
162950
- function canonicalRevocationPayload(r) {
162951
- return JSON.stringify({ grantId: r.grantId, grantVersion: r.grantVersion, revokedAt: r.revokedAt, by: r.by });
162952
- }
162953
- function presetForChoice(choice) {
162954
- const base6 = { channels: "both", replyWakeAllowed: true, autoSend: true, newRecipientAllowed: false };
162955
- switch (choice) {
162956
- case "task_once":
162957
- return { ...base6, level: "task_once", contactScope: "verified_contacts", taskScope: "current_goal", contentScope: "normal", autoSend: false, sensitiveContentAllowed: false };
162958
- case "persistent":
162959
- return { ...base6, level: "persistent", contactScope: "verified_contacts", taskScope: "all_future_goals", contentScope: "normal", sensitiveContentAllowed: false };
162960
- case "full_contact_access":
162961
- return { ...base6, level: "full_contact_access", contactScope: "all_contacts", taskScope: "all_future_goals", contentScope: "sensitive", sensitiveContentAllowed: true };
162962
- }
163392
+ function isIso(s2) {
163393
+ return typeof s2 === "string" && s2.length >= 20 && Number.isFinite(Date.parse(s2));
163394
+ }
163395
+ function checkTaskActionFreshness(a, now) {
163396
+ if (!isIso(a?.createdAt) || !isIso(a?.expiresAt)) return { ok: false, reason: "bad_time" };
163397
+ const created = Date.parse(a.createdAt);
163398
+ const expires = Date.parse(a.expiresAt);
163399
+ if (expires - created > TASK_ACTION_TTL_MS) return { ok: false, reason: "ttl_too_long" };
163400
+ if (expires < created) return { ok: false, reason: "bad_time" };
163401
+ if (now > expires) return { ok: false, reason: "expired" };
163402
+ if (now < created - TASK_ACTION_CLOCK_SKEW_MS) return { ok: false, reason: "not_yet_valid" };
163403
+ return { ok: true };
162963
163404
  }
162964
163405
 
162965
- // src/web/mobile-contacts.ts
162966
- var K_DEVICE = "bolloon.contacts.device.v1";
162967
- var K_QUEUE = "bolloon.contacts.queue.v1";
162968
- var K_CAPS = "bolloon.contacts.capabilities.v1";
162969
- var cryptoOf = (c) => c || globalThis.crypto || null;
162970
- function ed25519Available(c) {
162971
- const cobj = cryptoOf(c);
162972
- if (!cobj?.subtle) return false;
162973
- try {
162974
- const alg = { name: "Ed25519" };
162975
- return typeof cobj.subtle.importKey === "function" && !!alg;
162976
- } catch {
162977
- return false;
162978
- }
163406
+ // src/agents/task-public-text.ts
163407
+ var PRIVACY_RULES = [
163408
+ { rule: "wallet-address", why: "\u4EE5\u592A\u574A/EVM \u94B1\u5305\u5730\u5740", re: /(?<![0-9a-fA-FxX])0x[0-9a-fA-F]{40}(?![0-9a-fA-F])/ },
163409
+ { rule: "private-key-hex", why: "32 \u5B57\u8282\u79C1\u94A5\u5F62\u6001 (0x + 64 hex)", re: /(?<![0-9a-fA-FxX])0x[0-9a-fA-F]{64}(?![0-9a-fA-F])/ },
163410
+ { rule: "hex-0x-blob", why: "0x \u5F00\u5934\u7684\u5341\u516D\u8FDB\u5236\u5757 (\u7B7E\u540D/\u54C8\u5E0C/\u5BC6\u94A5\u90FD\u957F\u8FD9\u6837, \u7FA4\u91CC\u4E00\u5F8B\u4E0D\u653E\u539F\u6587)", re: /(?<![0-9a-fA-FxX])0x[0-9a-fA-F]{8,}(?![0-9a-fA-F])/ },
163411
+ { rule: "did", why: "\u53BB\u4E2D\u5FC3\u5316\u8EAB\u4EFD DID", re: /did:[a-z0-9]+:[A-Za-z0-9._:%-]{3,}/i },
163412
+ { rule: "peer-id", why: "libp2p peerId (12D3Koo\u2026/Qm\u2026)", re: /12D3Koo[A-Za-z0-9]{8,}|Qm[1-9A-HJ-NP-Za-km-z]{30,}/ },
163413
+ { rule: "multiaddr", why: "libp2p multiaddr \u6216 store \u5730\u5740 (/ip4 /tcp /p2p /orbitdb \u2026)", re: /\/(ip4|ip6|dns|dns4|dns6|tcp|udp|ws|wss|quic|p2p|p2p-circuit|ipfs|orbitdb)(?![A-Za-z0-9])/i },
163414
+ { rule: "orbitdb-link", why: "OrbitDB \u94FE\u63A5 (\u7FA4\u9080\u8BF7\u94FE\u63A5\u672C\u8EAB\u4E5F\u4E0D\u8FDB\u7FA4\u6D88\u606F)", re: /orbitdb:\/\//i },
163415
+ { rule: "ipv4", why: "IPv4 \u5730\u5740", re: /(?<!\d)\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}(?!\d)/ },
163416
+ { rule: "ipv6", why: "IPv6 \u5730\u5740 (\u4E0D\u542B ISO \u65F6\u95F4\u6233)", re: /(?<![0-9A-Za-z])(?:[0-9a-fA-F]{1,4})?::(?:[0-9a-fA-F]{1,4}:){0,6}[0-9a-fA-F]{1,4}(?![0-9A-Za-z])|(?<![0-9A-Za-z:])(?:[0-9a-fA-F]{1,4}:){3,}[0-9a-fA-F]{1,4}(?![0-9A-Za-z])/ },
163417
+ { rule: "pem", why: "PEM \u6750\u6599 (\u79C1\u94A5/\u8BC1\u4E66)", re: /-----BEGIN [A-Z ]*(PRIVATE KEY|CERTIFICATE)/ },
163418
+ { rule: "secret-words", why: "\u79C1\u94A5/\u52A9\u8BB0\u8BCD/\u5BC6\u94A5\u5B57\u6837 (\u6B63\u6587\u91CC\u63D0\u5230\u5373\u505C, \u4EBA\u4E0D\u8BE5\u628A\u5BC6\u94A5\u5199\u8FDB\u7FA4)", re: /(private\s*key|seed\s*phrase|mnemonic|私钥|助记词|密钥材料)/i },
163419
+ { rule: "url", why: "http(s) \u7AEF\u70B9 (\u7AEF\u70B9\u4F1A\u5E26 host/IP \u4E0E\u7528\u9014\u6307\u7EB9)", re: /https?:\/\/\S+/i },
163420
+ { rule: "email", why: "\u90AE\u7BB1\u5730\u5740", re: /[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}/ }
163421
+ ];
163422
+ function mask(s2) {
163423
+ const t = String(s2 || "");
163424
+ return t.length <= 4 ? "****" : `${t.slice(0, 4)}\u2026`;
162979
163425
  }
162980
- function bytesOf(s2) {
162981
- const src4 = new TextEncoder().encode(s2);
162982
- const out = new Uint8Array(new ArrayBuffer(src4.byteLength));
162983
- out.set(src4);
163426
+ function scanPublicText(text) {
163427
+ const t = String(text ?? "");
163428
+ const out = [];
163429
+ const seen = /* @__PURE__ */ new Set();
163430
+ for (const r of PRIVACY_RULES) {
163431
+ const m2 = r.re.exec(t);
163432
+ if (!m2) continue;
163433
+ if (seen.has(r.rule)) continue;
163434
+ seen.add(r.rule);
163435
+ out.push({ rule: r.rule, why: r.why, masked: mask(String(m2[0])), chars: String(m2[0]).length });
163436
+ }
162984
163437
  return out;
162985
163438
  }
162986
- function b64(buf) {
162987
- const arr = new Uint8Array(buf);
162988
- let out = "";
162989
- for (let i = 0; i < arr.length; i++) out += String.fromCharCode(arr[i]);
162990
- return btoa(out);
162991
- }
162992
- function pemFromSpki(spki) {
162993
- const b = b64(spki).replace(/(.{64})/g, "$1\n").trim();
162994
- return `-----BEGIN PUBLIC KEY-----
162995
- ${b}
162996
- -----END PUBLIC KEY-----
162997
- `;
163439
+ function requirePublicText(text) {
163440
+ const v = scanPublicText(text);
163441
+ return v.length === 0 ? { ok: true } : { ok: false, violations: v };
163442
+ }
163443
+ var NODE_IDENTITY_RULES = PRIVACY_RULES.filter((r) => r.rule !== "orbitdb-link").map((r) => r.rule === "multiaddr" ? { ...r, re: /\/(ip4|ip6|dns|dns4|dns6|tcp|udp|ws|wss|quic|p2p|p2p-circuit|ipfs)(?![A-Za-z0-9])/i } : r);
163444
+
163445
+ // src/agents/mobile-task-views.ts
163446
+ function shortId(id, n = 12) {
163447
+ const s2 = String(id ?? "").trim();
163448
+ if (!s2) return "";
163449
+ return s2.length <= n ? s2 : `${s2.slice(0, n)}\u2026`;
163450
+ }
163451
+ function isProtocolId(s2) {
163452
+ const v = String(s2 ?? "").trim();
163453
+ return /^ann-[0-9a-f]{8,}$/i.test(v) || /^act-[a-z0-9-]{6,}$/i.test(v) || /^zdpu[0-9a-zA-Z]{8,}$/.test(v) || /^\/orbitdb\//.test(v);
163454
+ }
163455
+ function formatRelative(ms2, lang) {
163456
+ if (ms2 === null || !Number.isFinite(ms2)) return lang === "zh" ? "\u2014" : "\u2014";
163457
+ const past = ms2 >= 0;
163458
+ const abs = Math.abs(ms2);
163459
+ const min = Math.floor(abs / 6e4);
163460
+ const hr = Math.floor(abs / 36e5);
163461
+ const day = Math.floor(abs / 864e5);
163462
+ const unit = day >= 1 ? lang === "zh" ? `${day} \u5929` : `${day} d` : hr >= 1 ? lang === "zh" ? `${hr} \u5C0F\u65F6` : `${hr} h` : min >= 1 ? lang === "zh" ? `${min} \u5206\u949F` : `${min} min` : lang === "zh" ? "\u4E0D\u5230 1 \u5206\u949F" : "under a minute";
163463
+ if (lang === "zh") return past ? `${unit}\u524D` : `${unit}\u540E`;
163464
+ return past ? `${unit} ago` : `in ${unit}`;
163465
+ }
163466
+ var STATUS_LABELS = {
163467
+ open: { zh: "\u53EF\u63A5\u5355", en: "Open for claims" },
163468
+ claimed: { zh: "\u5DF2\u88AB\u63A5\u5355", en: "Claimed" },
163469
+ cancelled: { zh: "\u5DF2\u53D6\u6D88", en: "Cancelled" },
163470
+ unknown: { zh: "\u72B6\u6001\u672A\u77E5", en: "Unknown status" }
163471
+ };
163472
+ function budgetLabelOf(b) {
163473
+ if (!b || typeof b !== "object") return "";
163474
+ const amt = String(b.maxAmount ?? "").trim();
163475
+ if (!amt) return "";
163476
+ return `${amt} ${String(b.currency || "").toUpperCase()}${b.network ? ` \xB7 ${String(b.network)}` : ""}`;
163477
+ }
163478
+ function boardItemOf(e, now) {
163479
+ const dl = Number.isFinite(Number(e?.deadline)) ? Number(e.deadline) : null;
163480
+ const ca = Number.isFinite(Number(e?.createdAt)) ? Number(e.createdAt) : null;
163481
+ const status = String(e?.status || "unknown");
163482
+ return {
163483
+ idShort: shortId(e?.announcementId),
163484
+ ref: String(e?.announcementId || ""),
163485
+ capability: String(e?.capability || ""),
163486
+ status,
163487
+ statusLabel: STATUS_LABELS[status] || STATUS_LABELS.unknown,
163488
+ budgetLabel: budgetLabelOf(e?.budget),
163489
+ deadlineInMs: dl === null ? null : dl - now,
163490
+ createdAtMs: ca,
163491
+ claimCount: Number.isFinite(Number(e?.claimCount)) ? Number(e.claimCount) : 0,
163492
+ preview: e?.instructionPreview ? String(e.instructionPreview).slice(0, 120) : null,
163493
+ remote: !!e?.remote,
163494
+ claimable: !!e?.claimable,
163495
+ signatureVerified: e?.signatureVerified === true ? true : e?.signatureVerified === false ? false : null
163496
+ };
162998
163497
  }
162999
- async function sha256Hex(c, text) {
163000
- const d2 = await c.subtle.digest("SHA-256", bytesOf(text));
163001
- return [...new Uint8Array(d2)].map((x) => x.toString(16).padStart(2, "0")).join("");
163498
+ function buildBoardView(view, now) {
163499
+ const items = (Array.isArray(view?.entries) ? view.entries : []).map((e) => boardItemOf(e, now));
163500
+ return {
163501
+ items,
163502
+ localCount: Number(view?.localCount) || 0,
163503
+ remoteCount: Number(view?.remoteCount) || 0,
163504
+ registryReady: view?.registryReady === true,
163505
+ registryError: view?.registryError ? String(view.registryError).slice(0, 160) : null,
163506
+ notes: Array.isArray(view?.notes) ? view.notes.map((n) => String(n)) : [],
163507
+ openCount: items.filter((i) => i.claimable).length
163508
+ };
163002
163509
  }
163003
- async function loadOrCreateDeviceKey(storage2, c) {
163004
- const cobj = cryptoOf(c);
163005
- if (!cobj?.subtle) throw new Error("device_signing_unavailable: \u672C\u673A\u6CA1\u6709 WebCrypto");
163006
- const raw = storage2.getItem(K_DEVICE);
163007
- if (raw) {
163008
- try {
163009
- const parsed = JSON.parse(raw);
163010
- if (parsed?.privateKeyJwk && parsed?.publicKeyPem && parsed?.deviceId) return parsed;
163011
- } catch {
163510
+ var TRAIL_KIND_LABELS = {
163511
+ announce: { zh: "\u516C\u544A", en: "Announce" },
163512
+ claim: { zh: "\u63A5\u5355\u58F0\u660E", en: "Claim" },
163513
+ deliver: { zh: "\u4EA4\u4ED8", en: "Delivery" },
163514
+ screen: { zh: "\u521D\u7B5B\u7ED3\u679C", en: "Screening" },
163515
+ final: { zh: "\u7EC8\u5BA1\u7ED3\u8BBA", en: "Final verdict" }
163516
+ };
163517
+ var INCONSISTENCY_LABELS = {
163518
+ "final-accept-without-delivery": { zh: "\u8BF4\u901A\u8FC7\u4E86, \u4F46\u7FA4\u91CC\u6CA1\u6709\u4EA4\u4ED8\u75D5\u8FF9", en: "Accepted, but no delivery trace in the group" },
163519
+ "screen-without-delivery": { zh: "\u6709\u521D\u7B5B\u7ED3\u679C, \u4F46\u6CA1\u6709\u4EA4\u4ED8\u75D5\u8FF9", en: "Screening exists without a delivery trace" },
163520
+ "claim-without-announce": { zh: "\u6709\u4EBA\u63A5\u5355, \u4F46\u7FA4\u91CC\u6CA1\u6709\u516C\u544A\u75D5\u8FF9", en: "Claim without an announcement trace" },
163521
+ "deliver-without-claim": { zh: "\u6709\u4EA4\u4ED8, \u4F46\u6CA1\u4EBA\u63A5\u8FC7\u5355", en: "Delivery without a claim" },
163522
+ "both-accept-and-reject": { zh: "\u540C\u65F6\u51FA\u73B0\u901A\u8FC7\u548C\u62D2\u7EDD", en: "Both accepted and rejected appear" },
163523
+ "group-message-hit-privacy-rule": { zh: "\u7FA4\u91CC\u6709\u4EBA\u53D1\u4E86\u5E26\u6807\u8BC6\u7B26\u7684\u6D88\u606F (\u5DF2\u906E\u853D, \u4E0D\u5F53\u4E8B\u5B9E\u91C7\u7528)", en: "A group message hit a privacy rule (masked, not used as fact)" }
163524
+ };
163525
+ function buildTrailView(summary) {
163526
+ const entries = (Array.isArray(summary?.entries) ? summary.entries : []).map((e) => {
163527
+ const facts = [];
163528
+ for (const [k, v] of Object.entries(e?.fields || {})) {
163529
+ if (k === "kind") continue;
163530
+ const raw = String(v ?? "");
163531
+ const hit = scanPublicText(raw);
163532
+ if (hit.length) {
163533
+ facts.push({ k: String(k), v: `[\u5DF2\u906E\u853D:${hit[0].rule}]` });
163534
+ continue;
163535
+ }
163536
+ facts.push({ k: String(k), v: k === "id" || isProtocolId(raw) ? shortId(raw) : raw });
163012
163537
  }
163013
- }
163014
- let pair;
163538
+ const kind = String(e?.kind || "");
163539
+ return {
163540
+ idShort: shortId(e?.announcementId),
163541
+ kind,
163542
+ kindLabel: TRAIL_KIND_LABELS[kind] || { zh: kind, en: kind },
163543
+ sender: String(e?.sender || "unknown"),
163544
+ atMs: Number.isFinite(Number(e?.at)) ? Number(e.at) : 0,
163545
+ facts
163546
+ };
163547
+ });
163548
+ const inc = (Array.isArray(summary?.inconsistencies) ? summary.inconsistencies : []).map((c) => ({
163549
+ code: String(c),
163550
+ label: INCONSISTENCY_LABELS[String(c)] || { zh: String(c), en: String(c) }
163551
+ }));
163552
+ return {
163553
+ count: Number(summary?.count) || entries.length,
163554
+ byKind: summary?.byKind || {},
163555
+ entries,
163556
+ announcementIdsShort: (Array.isArray(summary?.announcements) ? summary.announcements : []).map((a) => shortId(a)),
163557
+ flags: summary?.flags || {},
163558
+ inconsistencies: inc,
163559
+ redactedCount: Array.isArray(summary?.redacted) ? summary.redacted.length : 0,
163560
+ ignoredMessages: Number(summary?.ignoredMessages) || 0,
163561
+ privacyHits: inc.some((x) => x.code === "group-message-hit-privacy-rule") || Array.isArray(summary?.redacted) && summary.redacted.length > 0
163562
+ };
163563
+ }
163564
+ function buildGroupsView(groups) {
163565
+ return (Array.isArray(groups) ? groups : []).map((g) => ({
163566
+ idShort: shortId(g?.id),
163567
+ name: String(g?.name || ""),
163568
+ joinedAt: String(g?.createdAt || "")
163569
+ }));
163570
+ }
163571
+ function describeTaskActionForConfirm(kind, req) {
163572
+ const lines = [];
163573
+ const push = (k, kEn, v) => {
163574
+ const s2 = String(v ?? "").trim();
163575
+ if (s2 && s2 !== "-") lines.push({ k, kEn, v: s2 });
163576
+ };
163577
+ push("\u7FA4", "group", req?.groupRef ? shortId(req.groupRef, 24) : "");
163578
+ push("\u516C\u544A\u53F7", "announcement", req?.announcementId ? shortId(req.announcementId) : "");
163579
+ push("\u80FD\u529B", "capability", req?.capability);
163580
+ push("\u9884\u7B97", "budget", req?.budgetHuman ? `${req.budgetHuman} ${String(req?.currency || "").toUpperCase()}` : "");
163581
+ push("\u4EFB\u52A1\u6B63\u6587", "instruction", req?.instruction);
163582
+ push("\u622A\u6B62", "deadline", req?.deadline);
163583
+ push("\u9A8C\u6536\u5224\u636E", "criteria", req?.criteria);
163584
+ push("\u671F\u53F7", "round", req?.round);
163585
+ push("\u75D5\u8FF9\u7C7B\u578B", "trail kind", req?.trailKind ? TRAIL_KIND_LABELS[String(req.trailKind)]?.zh || String(req.trailKind) : "");
163586
+ push("\u58F0\u660E\u4EF7\u683C", "declared price", req?.price);
163587
+ push("\u4EA4\u4ED8\u54C8\u5E0C", "delivery hash", req?.hash);
163588
+ push("\u5B57\u8282\u6570", "bytes", req?.bytes);
163589
+ push("\u9010\u6761\u521D\u7B5B", "per-item screen", req?.checks);
163590
+ push("\u7EC8\u5BA1\u7ED3\u8BBA", "final verdict", req?.verdict);
163591
+ const titles = {
163592
+ group_join: { zh: "\u52A0\u5165\u8FD9\u4E2A\u7FA4", en: "Join this group" },
163593
+ group_leave: { zh: "\u9000\u51FA\u8FD9\u4E2A\u7FA4", en: "Leave this group" },
163594
+ group_create: { zh: "\u5EFA\u4E00\u4E2A\u65B0\u7FA4", en: "Create a new group" },
163595
+ announce_publish: { zh: "\u53D1\u5E03\u4EFB\u52A1\u516C\u544A", en: "Publish a task announcement" },
163596
+ announce_to_group: { zh: "\u628A\u8FD9\u671F\u516C\u544A\u53D1\u8FDB\u7FA4", en: "Post this announcement into the group" },
163597
+ trail_post: { zh: "\u5F80\u7FA4\u91CC\u7559\u4E00\u6761\u8FC7\u7A0B\u75D5\u8FF9", en: "Post a process trail entry into the group" }
163598
+ };
163599
+ const t = titles[String(kind)] || { zh: "\u6267\u884C\u8FD9\u4E2A\u52A8\u4F5C", en: "Run this action" };
163600
+ return { titleZh: t.zh, titleEn: t.en, lines };
163601
+ }
163602
+
163603
+ // src/agents/goal-flywheel/types.ts
163604
+ var MUST_NOT_EXPOSE_FIELDS = [
163605
+ "lease",
163606
+ "reducer",
163607
+ "internal_status",
163608
+ "retry_counter",
163609
+ "worker_owner"
163610
+ ];
163611
+
163612
+ // src/agents/goal-flywheel/work-monitor.ts
163613
+ var NO_PROGRESS_FALLBACK_MS = 30 * 60 * 1e3;
163614
+ var DEFAULT_REPORT_GRACE_MS = 60 * 1e3;
163615
+ var DEFAULT_ESCALATION_MS = 10 * 60 * 1e3;
163616
+ var TERMINAL_GOAL_STATES = ["completed", "failed", "abandoned"];
163617
+ var TERMINAL_GOAL_STATUSES = [...TERMINAL_GOAL_STATES];
163618
+
163619
+ // src/agents/mobile-flywheel-view.ts
163620
+ function findInternalFieldLeaks(text) {
163621
+ const t = String(text ?? "");
163622
+ return MUST_NOT_EXPOSE_FIELDS.filter((f) => t.includes(f));
163623
+ }
163624
+
163625
+ // src/web/mobile-tasks.ts
163626
+ init_mobile_contacts();
163627
+ var K_TASKS_HINT = "bolloon.mobile.tasks.hint.v1";
163628
+ function defaultStorage2() {
163015
163629
  try {
163016
- pair = await cobj.subtle.generateKey({ name: "Ed25519" }, true, ["sign", "verify"]);
163017
- } catch (err2) {
163018
- throw new Error(`device_signing_unavailable: ${String(err2?.message || err2)}`);
163630
+ if (typeof localStorage !== "undefined") return localStorage;
163631
+ } catch {
163019
163632
  }
163020
- const publicKeyPem = pemFromSpki(await cobj.subtle.exportKey("spki", pair.publicKey));
163021
- const privateKeyJwk = await cobj.subtle.exportKey("jwk", pair.privateKey);
163022
- const deviceId = `dev-${(await sha256Hex(cobj, publicKeyPem)).slice(0, 12)}`;
163023
- const material = { deviceId, publicKeyPem, privateKeyJwk };
163024
- storage2.setItem(K_DEVICE, JSON.stringify(material));
163025
- return material;
163633
+ const mem2 = /* @__PURE__ */ new Map();
163634
+ return { getItem: (k) => mem2.has(k) ? mem2.get(k) : null, setItem: (k, v) => {
163635
+ mem2.set(k, v);
163636
+ }, removeItem: (k) => {
163637
+ mem2.delete(k);
163638
+ } };
163026
163639
  }
163027
- async function importPrivate(storage2, c) {
163028
- const k = await loadOrCreateDeviceKey(storage2, c);
163029
- return c.subtle.importKey("jwk", k.privateKeyJwk, { name: "Ed25519" }, false, ["sign"]);
163030
- }
163031
- async function signGrantOnDevice(grant, storage2, c) {
163032
- const cobj = cryptoOf(c);
163033
- const key = await importPrivate(storage2, cobj);
163034
- const payload = canonicalGrantPayload(grant);
163035
- const sig = await cobj.subtle.sign({ name: "Ed25519" }, key, bytesOf(payload));
163036
- const dev = await loadOrCreateDeviceKey(storage2, cobj);
163037
- return { deviceId: dev.deviceId, alg: "ed25519", payloadHash: await sha256Hex(cobj, payload), sig: b64(sig) };
163640
+ async function baseOf(deps) {
163641
+ if (deps.base && String(deps.base).trim()) return String(deps.base).replace(/\/+$/, "");
163642
+ try {
163643
+ if (typeof localStorage !== "undefined") {
163644
+ const v = localStorage.getItem("bolloon_desktop_base_url");
163645
+ if (v) return String(v).replace(/\/+$/, "");
163646
+ }
163647
+ } catch {
163648
+ }
163649
+ return "";
163038
163650
  }
163039
- async function signRevocationOnDevice(rev, storage2, c) {
163040
- const cobj = cryptoOf(c);
163041
- const key = await importPrivate(storage2, cobj);
163042
- const payload = canonicalRevocationPayload(rev);
163043
- const sig = await cobj.subtle.sign({ name: "Ed25519" }, key, bytesOf(payload));
163044
- const dev = await loadOrCreateDeviceKey(storage2, cobj);
163045
- return { deviceId: dev.deviceId, alg: "ed25519", payloadHash: await sha256Hex(cobj, payload), sig: b64(sig) };
163651
+ function fetchOf(deps) {
163652
+ return deps.fetchImpl || globalThis.fetch;
163046
163653
  }
163047
- async function post(base6, path, body, f) {
163048
- const res = await f(`${String(base6).replace(/\/+$/, "")}${path}`, {
163049
- method: "POST",
163050
- headers: { "Content-Type": "application/json" },
163051
- body: JSON.stringify(body ?? {})
163052
- });
163654
+ async function getJson(base6, path, f) {
163655
+ const res = await f(`${base6}${path}`);
163053
163656
  let json = null;
163054
163657
  try {
163055
163658
  json = await res.json();
@@ -163057,8 +163660,12 @@ ${b}
163057
163660
  }
163058
163661
  return { status: res.status, json };
163059
163662
  }
163060
- async function get(base6, path, f) {
163061
- const res = await f(`${String(base6).replace(/\/+$/, "")}${path}`);
163663
+ async function postJson(base6, path, body, f) {
163664
+ const res = await f(`${base6}${path}`, {
163665
+ method: "POST",
163666
+ headers: { "Content-Type": "application/json" },
163667
+ body: JSON.stringify(body ?? {})
163668
+ });
163062
163669
  let json = null;
163063
163670
  try {
163064
163671
  json = await res.json();
@@ -163066,220 +163673,237 @@ ${b}
163066
163673
  }
163067
163674
  return { status: res.status, json };
163068
163675
  }
163069
- async function registerDeviceOnDesktop(base6, storage2, opts = {}) {
163070
- const f = opts.fetchImpl || fetch;
163071
- const dev = await loadOrCreateDeviceKey(storage2, opts.cryptoObj);
163072
- const r = await post(base6, "/api/contacts/devices", { deviceId: dev.deviceId, publicKeyPem: dev.publicKeyPem, label: opts.label || "\u624B\u673A" }, f);
163073
- if (r.status !== 200 || !r.json?.ok) return { ok: false, error: `\u684C\u9762\u672A\u63A5\u53D7\u8BBE\u5907\u767B\u8BB0: ${r.json?.error || r.status}`, data: dev };
163074
- return { ok: true, data: dev };
163676
+ var OFFLINE_NOTE = "\u5730\u5740\u5728 \u6211 \u2192 \u8BBE\u7F6E \u2192 \u7535\u8111\u7AEF\u5730\u5740 \u91CC\u586B; \u624B\u673A\u4E0D\u66FF\u684C\u9762\u8BB0\u8D26";
163677
+ function mapFail(r, what) {
163678
+ const code10 = r.json?.code ? String(r.json.code) : r.status ? `desktop_http_${r.status}` : "desktop_unreachable";
163679
+ return { ok: false, code: code10, error: `${what} \u5931\u8D25: ${r.json?.error || r.json?.message || code10}`, note: OFFLINE_NOTE };
163075
163680
  }
163076
- function saveCapabilityCopy(storage2, items) {
163077
- const clean8 = (items || []).map((c) => ({
163078
- contactId: String(c.contactId || ""),
163079
- kind: c.kind,
163080
- displayValue: String(c.displayValue || ""),
163081
- verificationStatus: String(c.verificationStatus || ""),
163082
- capabilities: [...c.capabilities || []],
163083
- provider: c.provider ? String(c.provider) : void 0,
163084
- state: c.state ? String(c.state) : void 0
163085
- }));
163086
- storage2.setItem(K_CAPS, JSON.stringify(clean8));
163681
+ function emptyTaskActionRequest(kind) {
163682
+ return {
163683
+ kind,
163684
+ groupRef: null,
163685
+ announcementId: null,
163686
+ capability: null,
163687
+ instruction: null,
163688
+ budgetHuman: null,
163689
+ currency: null,
163690
+ deadline: null,
163691
+ criteria: null,
163692
+ round: null,
163693
+ price: null,
163694
+ hash: null,
163695
+ bytes: null,
163696
+ checks: null,
163697
+ verdict: null,
163698
+ trailKind: null
163699
+ };
163087
163700
  }
163088
- function loadCapabilityCopy(storage2) {
163089
- try {
163090
- const v = JSON.parse(storage2.getItem(K_CAPS) || "[]");
163091
- return Array.isArray(v) ? v : [];
163092
- } catch {
163093
- return [];
163701
+ async function signTaskAction(req, deps = {}) {
163702
+ const storage2 = deps.storage || defaultStorage2();
163703
+ const c = deps.cryptoObj || globalThis.crypto;
163704
+ if (!ed25519Available(c)) {
163705
+ return { ok: false, code: "device_signing_unavailable", error: "\u672C\u673A WebView \u4E0D\u652F\u6301 Ed25519 \u8BBE\u5907\u7B7E\u540D \u2014\u2014 \u8BF7\u5728\u684C\u9762\u7AEF\u6267\u884C\u8FD9\u4E2A\u52A8\u4F5C (\u6216\u5347\u7EA7\u7CFB\u7EDF WebView)" };
163094
163706
  }
163095
- }
163096
- function pendingQueue(storage2) {
163097
- try {
163098
- const v = JSON.parse(storage2.getItem(K_QUEUE) || "[]");
163099
- return Array.isArray(v) ? v : [];
163100
- } catch {
163101
- return [];
163707
+ if (!MOBILE_TASK_ACTION_KINDS.includes(String(req?.kind))) {
163708
+ return { ok: false, code: "unknown_kind", error: `\u672A\u77E5\u52A8\u4F5C: ${String(req?.kind || "")}` };
163102
163709
  }
163103
- }
163104
- function setQueue(storage2, q) {
163105
- storage2.setItem(K_QUEUE, JSON.stringify(q.slice(-20)));
163106
- }
163107
- async function flushQueuedGrants(base6, storage2, opts = {}) {
163108
- const f = opts.fetchImpl || fetch;
163109
- const q = pendingQueue(storage2);
163110
- const keep = [];
163111
- const results = [];
163112
- let sent = 0, failed = 0;
163113
- for (const item of q) {
163114
- try {
163115
- const r = await post(base6, "/api/contacts/grants/sync", { grant: { ...item.grant, signature: item.signature } }, f);
163116
- if (r.status === 200 && r.json?.ok) {
163117
- sent++;
163118
- results.push({ grantId: item.grant.grantId, ok: true });
163119
- continue;
163710
+ const text = taskActionContentText(req);
163711
+ if (req.kind === "trail_post" || req.kind === "announce_publish" || req.kind === "announce_to_group") {
163712
+ for (const fieldText of [req.round, req.price, req.criteria, req.checks, req.verdict, req.capability, req.hash]) {
163713
+ if (!fieldText) continue;
163714
+ const gate = requirePublicText(String(fieldText));
163715
+ if (!gate.ok) {
163716
+ return { ok: false, code: "privacy_denied", error: `\u672C\u5730\u9690\u79C1\u95F8\u62E6\u4E0B: ${gate.violations.map((v) => v.rule).join(", ")} (\u7FA4\u91CC/\u516C\u544A\u91CC\u4E0D\u653E\u6807\u8BC6\u7B26)`, violations: gate.violations };
163120
163717
  }
163121
- failed++;
163122
- keep.push(item);
163123
- results.push({ grantId: item.grant.grantId, ok: false, error: r.json?.code || r.status });
163124
- } catch {
163125
- failed++;
163126
- keep.push(item);
163127
- results.push({ grantId: item.grant.grantId, ok: false, error: "network" });
163128
163718
  }
163129
163719
  }
163130
- setQueue(storage2, keep);
163131
- return { sent, failed, results };
163132
- }
163133
- async function authorizeFromPhone(input) {
163134
- const c = cryptoOf(input.cryptoObj);
163135
- if (!ed25519Available(c || void 0)) {
163136
- return { ok: false, error: "device_signing_unavailable: \u672C\u673A WebView \u4E0D\u652F\u6301 Ed25519 \u8BBE\u5907\u7B7E\u540D \u2014\u2014 \u8BF7\u5728\u684C\u9762\u7AEF\u6388\u6743 (\u6216\u5347\u7EA7\u7CFB\u7EDF WebView)" };
163137
- }
163138
- const dev = await loadOrCreateDeviceKey(input.storage, c);
163139
- const preset = presetForChoice(input.choice);
163140
- const grant = {
163141
- grantId: `gr-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`,
163142
- identityId: input.identityId || `sid-phone-${dev.deviceId.slice(-6)}`,
163143
- ownerDid: input.ownerDid,
163144
- ...preset,
163145
- grantedAt: (/* @__PURE__ */ new Date()).toISOString(),
163146
- grantedBy: input.grantedBy || "leo",
163147
- grantedVia: "mobile",
163148
- deviceIds: [dev.deviceId],
163149
- status: "active",
163150
- grantVersion: 1
163720
+ const now = deps.now ? deps.now() : Date.now();
163721
+ const ttl = Math.min(Number(deps.ttlMs) || TASK_ACTION_DEFAULT_TTL_MS, TASK_ACTION_DEFAULT_TTL_MS);
163722
+ let contentDigest;
163723
+ try {
163724
+ contentDigest = await sha256HexOf(text, c);
163725
+ } catch (e) {
163726
+ return { ok: false, code: "device_signing_unavailable", error: String(e?.message || e) };
163727
+ }
163728
+ const action = {
163729
+ actionId: `act-${now.toString(36)}-${Math.random().toString(36).slice(2, 8)}`,
163730
+ kind: req.kind,
163731
+ deviceId: "",
163732
+ // 由 signPayloadOnDevice 填 (它知道设备 id)
163733
+ ownerDid: String(deps.ownerDid || "").trim(),
163734
+ targetRef: String(req.groupRef || req.announcementId || ""),
163735
+ contentDigest,
163736
+ createdAt: new Date(now).toISOString(),
163737
+ expiresAt: new Date(now + ttl).toISOString(),
163738
+ grantedBy: deps.grantedBy || "leo",
163739
+ via: "mobile"
163151
163740
  };
163152
- const signature = await signGrantOnDevice(grant, input.storage, c);
163153
- const f = input.fetchImpl || fetch;
163154
- const reg = await registerDeviceOnDesktop(input.base, input.storage, { fetchImpl: f, cryptoObj: c }).catch(() => ({ ok: false }));
163155
- if (!reg.ok) {
163156
- const q2 = pendingQueue(input.storage);
163157
- q2.push({ grant, signature, queuedAt: (/* @__PURE__ */ new Date()).toISOString() });
163158
- setQueue(input.storage, q2);
163159
- return {
163160
- ok: false,
163161
- queued: true,
163162
- error: `\u684C\u9762\u6682\u4E0D\u53EF\u8FBE \u2192 \u6388\u6743\u5DF2\u5B58\u5728\u624B\u673A, \u5F85\u684C\u9762\u5728\u7EBF\u65F6\u81EA\u52A8\u540C\u6B65 (${reg.error || "offline"})`,
163163
- data: { grantId: grant.grantId, level: grant.level, signature },
163164
- note: "\u684C\u9762\u4E0D\u5728\u7EBF\u65F6\u4E0D\u5047\u88C5\u5DF2\u751F\u6548: \u6388\u6743\u5728\u672C\u5730\u961F\u5217\u91CC, \u684C\u9762\u4E00\u4E0A\u7EBF\u5C31\u8865\u540C\u6B65"
163165
- };
163166
- }
163167
- const r = await post(input.base, "/api/contacts/grants/sync", { grant: { ...grant, signature } }, f);
163168
- if (r.status === 200 && r.json?.ok) {
163169
- return { ok: true, data: { grantId: grant.grantId, level: grant.level, signature }, note: "\u684C\u9762\u5DF2\u9A8C\u7B7E\u63A5\u53D7" };
163741
+ let signature;
163742
+ try {
163743
+ const { loadOrCreateDeviceKey: loadOrCreateDeviceKey2, signPayloadWithMaterial: signPayloadWithMaterial2 } = await Promise.resolve().then(() => (init_mobile_contacts(), mobile_contacts_exports));
163744
+ const material = await loadOrCreateDeviceKey2(storage2, c);
163745
+ action.deviceId = material.deviceId;
163746
+ signature = await signPayloadWithMaterial2(canonicalTaskActionPayload(action), material, c);
163747
+ if (signature.deviceId !== action.deviceId) {
163748
+ return { ok: false, code: "device_signing_unavailable", error: "\u8BBE\u5907\u5BC6\u94A5\u4E0D\u4E00\u81F4 (\u7B7E\u540D\u4E0E\u8F7D\u8377\u8BBE\u5907\u5BF9\u4E0D\u4E0A) \u2192 \u4E0D\u53D1\u9001" };
163749
+ }
163750
+ const fresh = checkTaskActionFreshness(action, now);
163751
+ if (!fresh.ok) return { ok: false, code: fresh.reason, error: `\u52A8\u4F5C\u65F6\u6548\u4E0D\u5408\u6CD5: ${fresh.reason}` };
163752
+ } catch (e) {
163753
+ return { ok: false, code: "device_signing_unavailable", error: String(e?.message || e).slice(0, 160) };
163170
163754
  }
163171
- const q = pendingQueue(input.storage);
163172
- q.push({ grant, signature, queuedAt: (/* @__PURE__ */ new Date()).toISOString() });
163173
- setQueue(input.storage, q);
163174
163755
  return {
163175
- ok: false,
163176
- queued: true,
163177
- error: `\u684C\u9762\u62D2\u7EDD/\u672A\u63A5\u53D7: ${r.json?.code || r.json?.reason || r.status}`,
163178
- data: { grantId: grant.grantId, level: grant.level, signature },
163179
- note: "\u5DF2\u4FDD\u5B58\u5728\u624B\u673A\u672C\u5730\u961F\u5217 (\u4E0D\u4F1A\u56E0\u4E3A\u5931\u8D25\u5C31\u5F53\u6210\u5DF2\u6388\u6743)"
163756
+ ok: true,
163757
+ data: { signed: { action, signature }, contentText: text, confirm: describeTaskActionForConfirm(req.kind, req) }
163180
163758
  };
163181
163759
  }
163182
- async function revokeFromPhone(opts) {
163183
- const c = cryptoOf(opts.cryptoObj);
163184
- if (!ed25519Available(c || void 0)) return { ok: false, error: "device_signing_unavailable: \u672C\u673A\u4E0D\u652F\u6301\u8BBE\u5907\u7B7E\u540D, \u64A4\u9500\u9700\u5728\u684C\u9762\u7AEF\u8FDB\u884C" };
163185
- const revokedAt = (/* @__PURE__ */ new Date()).toISOString();
163186
- const payload = { grantId: opts.grantId, grantVersion: opts.version || 1, revokedAt, by: "mobile" };
163187
- const signature = await signRevocationOnDevice(payload, opts.storage, c);
163188
- const f = opts.fetchImpl || fetch;
163189
- const r = await post(opts.base, "/api/contacts/grants/revoke-sync", { grantId: opts.grantId, by: "mobile", reason: opts.reason, version: payload.grantVersion, revokedAt, signature }, f);
163190
- if (r.status === 200 && r.json?.ok) return { ok: true, data: { grantId: opts.grantId }, note: "\u684C\u9762\u5DF2\u9A8C\u7B7E\u5E76\u7ACB\u5373\u5931\u6548" };
163191
- return { ok: false, error: `\u64A4\u9500\u672A\u9001\u8FBE: ${r.json?.code || r.json?.error || r.status}`, note: "\u684C\u9762\u4E0D\u5728\u7EBF\u65F6\u64A4\u9500**\u4E0D\u4F1A**\u88AB\u5F53\u4F5C\u5DF2\u5B8C\u6210 \u2014\u2014 \u8BF7\u5728\u684C\u9762\u5728\u7EBF\u540E\u91CD\u8BD5 (\u6216\u684C\u9762\u7AEF\u64A4\u9500)" };
163760
+ async function executeTaskAction(req, deps = {}) {
163761
+ const storage2 = deps.storage || defaultStorage2();
163762
+ const base6 = await baseOf(deps);
163763
+ if (!base6) return { ok: false, code: "desktop_offline", error: "\u8FD8\u6CA1\u8BBE\u7F6E\u7535\u8111\u7AEF\u5730\u5740: \u9AD8\u98CE\u9669\u52A8\u4F5C\u5FC5\u987B\u7531\u684C\u9762\u6267\u884C (\u624B\u673A\u7B7E\u4E86\u540D\u4E5F\u8981\u9001\u5230\u684C\u9762)", note: OFFLINE_NOTE };
163764
+ const signed = await signTaskAction(req, { ...deps, storage: storage2 });
163765
+ if (!signed.ok || !signed.data) return { ok: false, code: signed.code, error: signed.error, violations: signed.violations };
163766
+ const f = fetchOf(deps);
163767
+ let r;
163768
+ try {
163769
+ r = await postJson(base6, "/api/mobile/tasks/execute", { request: req, ...signed.data.signed }, f);
163770
+ } catch (e) {
163771
+ return { ok: false, code: "desktop_offline", error: `\u684C\u9762\u4E0D\u53EF\u8FBE: ${String(e?.message || e).slice(0, 120)}`, note: OFFLINE_NOTE };
163772
+ }
163773
+ if (r.status !== 200 || !r.json?.ok) return mapFail(r, "\u6267\u884C");
163774
+ return { ok: true, data: r.json, note: "\u684C\u9762\u5DF2\u9A8C\u7B7E\u5E76\u6267\u884C (\u540C\u4E00\u5957\u5B58\u50A8\u4E0E\u534F\u8BAE)" };
163192
163775
  }
163193
- async function loadFromDesktop(base6, storage2, opts = {}) {
163194
- const f = opts.fetchImpl || fetch;
163195
- if (!base6) {
163196
- const caps = loadCapabilityCopy(storage2);
163197
- return {
163198
- ok: false,
163199
- error: "desktop_offline",
163200
- data: { contacts: caps, grants: [], effective: "\u684C\u9762\u4E0D\u5728\u7EBF (\u663E\u793A\u624B\u673A\u672C\u5730\u526F\u672C)", approvals: [], waiting: [] },
163201
- note: "\u4EE5\u4E0B\u662F\u4F60\u624B\u673A\u4E0A\u4FDD\u5B58\u7684\u80FD\u529B\u526F\u672C (\u4E0D\u662F\u684C\u9762\u5B9E\u65F6\u72B6\u6001)"
163202
- };
163776
+ async function previewGroupMessage(req, deps = {}) {
163777
+ const base6 = await baseOf(deps);
163778
+ if (!base6) return { ok: false, code: "desktop_offline", error: "\u9884\u89C8\u9700\u8981\u684C\u9762\u5728\u7EBF (\u7FA4\u6D88\u606F\u7531\u684C\u9762\u7684\u6784\u9020\u5668\u751F\u6210)", note: OFFLINE_NOTE };
163779
+ const c = deps.cryptoObj || globalThis.crypto;
163780
+ let digestPreview = "";
163781
+ try {
163782
+ digestPreview = await sha256HexOf(taskActionContentText(req), c);
163783
+ } catch {
163784
+ digestPreview = "";
163203
163785
  }
163786
+ const f = fetchOf(deps);
163204
163787
  try {
163205
- const a = await get(base6, "/api/contacts", f);
163206
- const b = await get(base6, "/api/contacts/grants", f);
163207
- if (a.status !== 200 && b.status !== 200) return { ok: false, error: `desktop_http_${a.status || b.status}` };
163208
- const view = {
163209
- contacts: a.json?.contacts || [],
163210
- grants: b.json?.grants || [],
163211
- effective: b.json?.effective || "",
163212
- approvals: a.json?.approvals || [],
163213
- waiting: a.json?.waiting || []
163214
- };
163215
- saveCapabilityCopy(storage2, (view.contacts || []).filter((c) => c.capabilities?.includes("send")).map((c) => ({
163216
- contactId: c.contactId,
163217
- kind: c.kind,
163218
- displayValue: c.displayValue,
163219
- verificationStatus: c.status,
163220
- capabilities: c.capabilities,
163221
- provider: c.provider,
163222
- state: c.status
163223
- })));
163224
- return { ok: true, data: view };
163225
- } catch (err2) {
163226
- const caps = loadCapabilityCopy(storage2);
163227
- return {
163228
- ok: false,
163229
- error: "desktop_offline",
163230
- data: { contacts: caps, grants: [], effective: "\u684C\u9762\u4E0D\u5728\u7EBF (\u663E\u793A\u624B\u673A\u672C\u5730\u526F\u672C)", approvals: [], waiting: [] },
163231
- note: `\u8FDE\u4E0D\u4E0A\u684C\u9762 (${String(err2?.message || err2).slice(0, 60)}) \u2014\u2014 \u663E\u793A\u672C\u5730\u526F\u672C`
163232
- };
163788
+ const r = await postJson(base6, "/api/mobile/tasks/preview", { request: req }, f);
163789
+ if (r.status !== 200 || !r.json?.ok) return mapFail(r, "\u9884\u89C8");
163790
+ return { ok: true, data: { text: String(r.json.text || ""), contentText: taskActionContentText(req), digestPreview } };
163791
+ } catch (e) {
163792
+ return { ok: false, code: "desktop_offline", error: `\u684C\u9762\u4E0D\u53EF\u8FBE: ${String(e?.message || e).slice(0, 120)}`, note: OFFLINE_NOTE };
163233
163793
  }
163234
163794
  }
163235
- async function bindFromPhone(base6, opts, storage2, f) {
163236
- const impl = f || fetch;
163237
- if (!base6) return { ok: false, error: "desktop_offline: \u7ED1\u5B9A\u9700\u8981\u684C\u9762\u7AEF\u5728\u7EBF (\u684C\u9762\u8D1F\u8D23\u843D\u76D8\u4E0E\u9A8C\u8BC1\u7801\u901A\u9053)" };
163238
- const r = await post(base6, "/api/contacts/bind", { kind: opts.kind, value: opts.value, region: opts.region }, impl);
163239
- if (r.status === 200 && r.json?.ok) return { ok: true, data: r.json };
163240
- return { ok: false, error: r.json?.error || `HTTP ${r.status}` };
163795
+ async function listGroups(deps = {}) {
163796
+ const base6 = await baseOf(deps);
163797
+ if (!base6) return { ok: false, code: "desktop_offline", error: "\u770B\u7FA4\u5217\u8868\u9700\u8981\u7535\u8111\u7AEF\u5728\u7EBF (\u7FA4\u5B58\u5728\u4E8E\u684C\u9762\u8282\u70B9\u7684 OrbitDB)", note: OFFLINE_NOTE };
163798
+ try {
163799
+ const r = await getJson(base6, "/api/mobile/tasks/groups", fetchOf(deps));
163800
+ if (r.status !== 200 || !r.json?.ok) return mapFail(r, "\u8BFB\u7FA4\u5217\u8868");
163801
+ return { ok: true, data: buildGroupsView(r.json.groups || []) };
163802
+ } catch (e) {
163803
+ return { ok: false, code: "desktop_offline", error: `\u684C\u9762\u4E0D\u53EF\u8FBE: ${String(e?.message || e).slice(0, 120)}`, note: OFFLINE_NOTE };
163804
+ }
163241
163805
  }
163242
- async function verifyFromPhone(base6, opts, storage2, f) {
163243
- const impl = f || fetch;
163244
- if (!base6) return { ok: false, error: "desktop_offline: \u9A8C\u8BC1\u9700\u8981\u684C\u9762\u7AEF\u5728\u7EBF" };
163245
- const r = await post(base6, "/api/contacts/verify", { contactId: opts.contactId, challengeId: opts.challengeId, code: opts.code }, impl);
163246
- if (r.status === 200 && r.json?.ok) return { ok: true, data: r.json };
163247
- return { ok: false, error: r.json?.error || `HTTP ${r.status}` };
163806
+ function joinGroup(link, deps = {}) {
163807
+ const req = { ...emptyTaskActionRequest("group_join"), groupRef: String(link || "").trim() };
163808
+ return executeTaskAction(req, deps);
163248
163809
  }
163249
- async function decideApprovalFromPhone(base6, opts, f) {
163250
- const impl = f || fetch;
163251
- if (!base6) return { ok: false, error: "desktop_offline: \u6279\u51C6\u9700\u8981\u684C\u9762\u7AEF\u5728\u7EBF (\u624B\u673A\u4E0D\u5728\u7EBF\u65F6\u4E0D\u4F1A\u5047\u88C5\u5DF2\u6279\u51C6)" };
163252
- const r = await post(base6, `/api/contacts/approvals/${encodeURIComponent(opts.consentId)}/${opts.action}`, { by: "leo", via: "mobile", body: opts.body, reason: opts.reason }, impl);
163253
- if (r.status === 200 && r.json?.ok) return { ok: true, data: r.json };
163254
- return { ok: false, error: r.json?.error || `HTTP ${r.status}` };
163810
+ function leaveGroup(groupRef, deps = {}) {
163811
+ const req = { ...emptyTaskActionRequest("group_leave"), groupRef: String(groupRef || "").trim() };
163812
+ return executeTaskAction(req, deps);
163255
163813
  }
163256
- function buildPhoneCard(view, opts) {
163257
- return {
163258
- title: "\u8BA9 Bolloon \u4EE3\u8868\u4F60\u8054\u7CFB\u5916\u90E8\u7684\u4EBA",
163259
- effective: view?.effective || opts.offline || "\u672A\u77E5",
163260
- contacts: (view?.contacts || []).map((c) => ({
163261
- displayValue: c.displayValue || "",
163262
- kind: c.kind || "",
163263
- state: c.status || c.verificationStatus || "",
163264
- label: c.capabilities?.includes("send") ? "\u53EF\u8054\u7CFB" : "\u4E0D\u53EF\u8054\u7CFB"
163265
- })),
163266
- approvals: (view?.approvals || []).map((a) => ({
163267
- consentId: a.consentId,
163268
- contactName: a.contactName,
163269
- channel: a.channel,
163270
- subject: a.subject,
163271
- bodyPreview: a.bodyPreview || "",
163272
- reason: a.reason,
163273
- reallySent: !!a.reallySent
163274
- })),
163275
- choices: [
163276
- { id: "task_once", label: "\u4EC5\u672C\u6B21\u4EFB\u52A1" },
163277
- { id: "persistent", label: "\u957F\u671F\u4F7F\u7528 (\u63A8\u8350)" },
163278
- { id: "full_contact_access", label: "\u5B8C\u5168\u6388\u6743\u8054\u7CFB\u65B9\u5F0F\u80FD\u529B" }
163279
- ],
163280
- willNotGet: ["\u8BFB\u53D6\u5168\u90E8\u90AE\u7BB1", "\u8BFB\u53D6\u901A\u8BAF\u5F55", "\u7FA4\u53D1\u6D88\u606F", "\u81EA\u52A8\u652F\u4ED8/\u8F6C\u8D26", "\u4EE3\u8868\u4F60\u7B7E\u7F72\u5408\u540C", "\u62FF\u5230\u624B\u673A\u53F7/\u90AE\u7BB1\u660E\u6587"],
163281
- deviceSigning: opts.deviceSigning
163814
+ function createGroup(name10, deps = {}) {
163815
+ const req = { ...emptyTaskActionRequest("group_create"), groupRef: String(name10 || "").trim() };
163816
+ return executeTaskAction(req, deps);
163817
+ }
163818
+ async function loadBoard(deps = {}) {
163819
+ const base6 = await baseOf(deps);
163820
+ if (!base6) return { ok: false, code: "desktop_offline", error: "\u770B\u677F\u9700\u8981\u7535\u8111\u7AEF\u5728\u7EBF (\u516C\u544A\u677F\u5728\u684C\u9762\u8282\u70B9\u4E0A)", note: OFFLINE_NOTE };
163821
+ const now = deps.now ? deps.now() : Date.now();
163822
+ try {
163823
+ const r = await getJson(base6, "/api/mobile/tasks/board", fetchOf(deps));
163824
+ if (r.status !== 200 || !r.json?.ok) return mapFail(r, "\u8BFB\u770B\u677F");
163825
+ return { ok: true, data: buildBoardView(r.json, now) };
163826
+ } catch (e) {
163827
+ return { ok: false, code: "desktop_offline", error: `\u684C\u9762\u4E0D\u53EF\u8FBE: ${String(e?.message || e).slice(0, 120)}`, note: OFFLINE_NOTE };
163828
+ }
163829
+ }
163830
+ function publishAnnouncement(input, deps = {}) {
163831
+ const req = {
163832
+ ...emptyTaskActionRequest("announce_publish"),
163833
+ capability: String(input.capability || "").trim(),
163834
+ instruction: String(input.instruction || ""),
163835
+ budgetHuman: String(input.budgetHuman || "").trim(),
163836
+ currency: String(input.currency || "USDC").toUpperCase(),
163837
+ deadline: input.deadline ? String(input.deadline).trim() : null
163838
+ };
163839
+ return executeTaskAction(req, deps);
163840
+ }
163841
+ function announceIntoGroup(input, deps = {}) {
163842
+ const req = {
163843
+ ...emptyTaskActionRequest("announce_to_group"),
163844
+ groupRef: String(input.groupRef || "").trim(),
163845
+ announcementId: String(input.announcementId || "").trim(),
163846
+ round: input.round ? String(input.round) : null,
163847
+ criteria: input.criteria ? String(input.criteria) : null
163848
+ };
163849
+ return executeTaskAction(req, deps);
163850
+ }
163851
+ function postTrail(input, deps = {}) {
163852
+ const req = {
163853
+ ...emptyTaskActionRequest("trail_post"),
163854
+ groupRef: String(input.groupRef || "").trim(),
163855
+ announcementId: String(input.announcementId || "").trim(),
163856
+ round: input.round ? String(input.round) : null,
163857
+ price: input.price ? String(input.price) : null,
163858
+ hash: input.hash ? String(input.hash) : null,
163859
+ bytes: typeof input.bytes === "number" ? input.bytes : null,
163860
+ checks: input.checks ? String(input.checks) : null,
163861
+ verdict: input.verdict ? String(input.verdict) : null,
163862
+ trailKind: input.kind
163282
163863
  };
163864
+ return executeTaskAction(req, deps);
163865
+ }
163866
+ async function loadTrail(input, deps = {}) {
163867
+ const base6 = await baseOf(deps);
163868
+ if (!base6) return { ok: false, code: "desktop_offline", error: "\u8BFB\u7FA4\u75D5\u8FF9\u9700\u8981\u7535\u8111\u7AEF\u5728\u7EBF", note: OFFLINE_NOTE };
163869
+ const q = new URLSearchParams();
163870
+ q.set("group", String(input.groupRef || "").trim());
163871
+ if (input.announcementId) q.set("announcementId", String(input.announcementId));
163872
+ if (input.limit) q.set("limit", String(input.limit));
163873
+ try {
163874
+ const r = await getJson(base6, `/api/mobile/tasks/trail?${q.toString()}`, fetchOf(deps));
163875
+ if (r.status !== 200 || !r.json?.ok) return mapFail(r, "\u8BFB\u7FA4\u75D5\u8FF9");
163876
+ return { ok: true, data: buildTrailView(r.json.summary || r.json) };
163877
+ } catch (e) {
163878
+ return { ok: false, code: "desktop_offline", error: `\u684C\u9762\u4E0D\u53EF\u8FBE: ${String(e?.message || e).slice(0, 120)}`, note: OFFLINE_NOTE };
163879
+ }
163880
+ }
163881
+ async function loadFlywheel(deps = {}) {
163882
+ const base6 = await baseOf(deps);
163883
+ if (!base6) return { ok: false, code: "desktop_offline", error: "\u770B\u98DE\u8F6E\u8FDB\u5EA6\u9700\u8981\u7535\u8111\u7AEF\u5728\u7EBF (Goal \u8BB0\u5F55\u5728\u684C\u9762\u8282\u70B9\u4E0A)", note: OFFLINE_NOTE };
163884
+ try {
163885
+ const r = await getJson(base6, "/api/mobile/tasks/flywheel", fetchOf(deps));
163886
+ if (r.status !== 200 || !r.json?.ok) return mapFail(r, "\u8BFB\u98DE\u8F6E\u8FDB\u5EA6");
163887
+ const goals = Array.isArray(r.json.goals) ? r.json.goals : [];
163888
+ const leaks = findInternalFieldLeaks(JSON.stringify(goals));
163889
+ if (leaks.length) return { ok: false, code: "view_leak", error: `\u89C6\u56FE\u91CC\u542B\u5185\u90E8\u5B57\u6BB5 (${leaks.join(", ")}) \u2192 \u62D2\u7EDD\u6E32\u67D3` };
163890
+ return { ok: true, data: { goals, hint: r.json.hint ? String(r.json.hint) : null } };
163891
+ } catch (e) {
163892
+ return { ok: false, code: "desktop_offline", error: `\u684C\u9762\u4E0D\u53EF\u8FBE: ${String(e?.message || e).slice(0, 120)}`, note: OFFLINE_NOTE };
163893
+ }
163894
+ }
163895
+ function setTaskHint(text, storage2 = defaultStorage2()) {
163896
+ try {
163897
+ storage2.setItem(K_TASKS_HINT, String(text || "").slice(0, 160));
163898
+ } catch {
163899
+ }
163900
+ }
163901
+ function getTaskHint(storage2 = defaultStorage2()) {
163902
+ try {
163903
+ return String(storage2.getItem(K_TASKS_HINT) || "");
163904
+ } catch {
163905
+ return "";
163906
+ }
163283
163907
  }
163284
163908
 
163285
163909
  // src/web/mobile-core.ts
@@ -164259,6 +164883,66 @@ ${b}
164259
164883
  return snap && Array.isArray(snap.skills) ? snap.skills : [];
164260
164884
  }
164261
164885
  },
164886
+ /**
164887
+ * 任务协作 (2026-09-25) — 手机与桌面/CLI 共用同一套存储与协议:
164888
+ * 入群 (`task group …`) · 发公告 (`task publish/announce/post`) · 看飞轮进度 (goal-flywheel 冻结类型, 只读)
164889
+ * 四个高风险动作 (入群/退群/建群/发公告/留痕) 一律**先签设备名再由桌面验签执行** —— 手机不自己执行、不记账。
164890
+ * 桌面不可达 → 明确失败 (任务动作**不进离线队列**: 几小时后突然生效的批准比失败更危险)。
164891
+ */
164892
+ tasks: {
164893
+ /** 本机能不能签 (老 WebView 没 Ed25519 → 高风险动作一律拒, 不降级) */
164894
+ deviceSigning() {
164895
+ return ed25519Available();
164896
+ },
164897
+ /** 桌面基址 (拿不到 → null, 不猜) */
164898
+ async desktopBase() {
164899
+ return core.contacts.desktopBase();
164900
+ },
164901
+ deps() {
164902
+ return { storage: core.contacts.storage(), ownerDid: void 0, grantedBy: "leo" };
164903
+ },
164904
+ /** ① 群: 列本机已加入的群 (脱敏, 只给缩短 id/群名/时间) */
164905
+ listGroups: () => listGroups(core.tasks.deps()),
164906
+ /** ① 入群 (高风险: 展示待发内容 → 设备签名 → 桌面验签执行) */
164907
+ joinGroup: (link) => joinGroup(link, core.tasks.deps()),
164908
+ /** ① 退群 (高风险) */
164909
+ leaveGroup: (ref) => leaveGroup(ref, core.tasks.deps()),
164910
+ /** ① 建群 (高风险; 建完自动入群) */
164911
+ createGroup: (name10) => createGroup(name10, core.tasks.deps()),
164912
+ /** ② 看板 (只读; 与 CLI `task board` 同一份事实) */
164913
+ loadBoard: () => loadBoard(core.tasks.deps()),
164914
+ /** ② 发布公告 (高风险) */
164915
+ publishAnnouncement: (input) => publishAnnouncement(input, core.tasks.deps()),
164916
+ /** ② 把公告发进群 (高风险; 预览 → 确认 → 签名) */
164917
+ announceIntoGroup: (input) => announceIntoGroup(input, core.tasks.deps()),
164918
+ /** ② 过程留痕 (高风险: claim/deliver/screen/final) */
164919
+ postTrail: (input) => postTrail(input, core.tasks.deps()),
164920
+ /** ② 读群里的一期痕迹 (只读) */
164921
+ loadTrail: (input) => loadTrail(input, core.tasks.deps()),
164922
+ /** 预览群消息那一行 (由桌面同一个构造器生成) */
164923
+ previewGroupMessage: (req) => previewGroupMessage(req, core.tasks.deps()),
164924
+ /** ③ 飞轮进度 (只读消费 goal-flywheel 冻结类型; 投影在桌面做) */
164925
+ loadFlywheel: () => loadFlywheel(core.tasks.deps()),
164926
+ /** 确认页文案 (展示待发内容) */
164927
+ describeConfirm: (kind, req) => describeTaskActionForConfirm(kind, req),
164928
+ /** 直接执行一个已构造好的请求 (UI 的统一入口: 签名 → 桌面验签执行) */
164929
+ execute: (req) => executeTaskAction(req, core.tasks.deps()),
164930
+ /** 相对时间人话 (毫秒差 → 双语; 只用于写文字节点) */
164931
+ relative: (ms2, lang) => formatRelative(ms2, lang === "en" ? "en" : "zh"),
164932
+ /** id 缩短 (页面上不出现原文) */
164933
+ shorten: (id, n) => shortId(id, n),
164934
+ /** 公开文本红线 (UI 最后一道自检: 渲染前扫一遍, 命中就不显示原文) */
164935
+ scanText: (text) => scanPublicText(String(text ?? "")),
164936
+ /** 构造一个空请求 (UI 填字段用) */
164937
+ emptyRequest: (kind) => emptyTaskActionRequest(kind),
164938
+ /** 最近一条操作提示 */
164939
+ hint() {
164940
+ return getTaskHint(core.contacts.storage());
164941
+ },
164942
+ setHint(text) {
164943
+ setTaskHint(text, core.contacts.storage());
164944
+ }
164945
+ },
164262
164946
  message: {
164263
164947
  async send({ text, channelId }) {
164264
164948
  if (!text || !channelId) return { ok: false, error: "text \u548C channelId \u5FC5\u586B" };