@agentunion/fastaun-browser 0.5.17 → 0.5.18

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 (60) hide show
  1. package/CHANGELOG.md +41 -0
  2. package/_packed_docs/CHANGELOG.md +41 -0
  3. package/_packed_docs/INDEX.md +28 -3
  4. package/_packed_docs/KITE_DOCS_GUIDE.md +11 -5
  5. package/_packed_docs/agent.md/SCHEMA.md +37 -18
  6. package/_packed_docs/aun/345/210/206/345/270/203/345/274/217/346/265/213/350/257/225/350/277/220/350/241/214/346/214/207/345/215/227.md +23 -17
  7. package/_packed_docs/aun/346/265/213/350/257/225/350/277/220/350/241/214/346/214/207/345/215/227.md +4 -2
  8. package/_packed_docs/protocol/01-/350/272/253/344/273/275/344/270/216/345/207/255/350/257/201/345/215/217/350/256/256-auth.md +95 -6
  9. package/_packed_docs/protocol/07-/351/224/231/350/257/257/347/240/201/344/270/216/347/212/266/346/200/201/346/234/272.md +19 -4
  10. package/_packed_docs/protocol/11-Storage-/345/255/220/345/215/217/350/256/256.md +25 -2
  11. package/_packed_docs/sdk/06-API/346/211/213/345/206/214.md +42 -4
  12. package/_packed_docs/sdk/09-storage-rpc-manual.md +25 -4
  13. package/_packed_docs/sdk/INDEX.md +3 -2
  14. package/dist/agent-md-schema.d.ts.map +1 -1
  15. package/dist/agent-md-schema.js +2 -0
  16. package/dist/agent-md-schema.js.map +1 -1
  17. package/dist/agent-md.d.ts +1 -1
  18. package/dist/agent-md.d.ts.map +1 -1
  19. package/dist/agent-md.js +6 -3
  20. package/dist/agent-md.js.map +1 -1
  21. package/dist/aid-store.d.ts +45 -0
  22. package/dist/aid-store.d.ts.map +1 -1
  23. package/dist/aid-store.js +231 -3
  24. package/dist/aid-store.js.map +1 -1
  25. package/dist/aid.d.ts +17 -1
  26. package/dist/aid.d.ts.map +1 -1
  27. package/dist/aid.js +49 -2
  28. package/dist/aid.js.map +1 -1
  29. package/dist/bundle.js +1315 -628
  30. package/dist/cert-utils.d.ts.map +1 -1
  31. package/dist/cert-utils.js +5 -3
  32. package/dist/cert-utils.js.map +1 -1
  33. package/dist/client/delivery.d.ts +1 -0
  34. package/dist/client/delivery.d.ts.map +1 -1
  35. package/dist/client/delivery.js +51 -7
  36. package/dist/client/delivery.js.map +1 -1
  37. package/dist/client/rpc-pipeline.d.ts.map +1 -1
  38. package/dist/client/rpc-pipeline.js +9 -1
  39. package/dist/client/rpc-pipeline.js.map +1 -1
  40. package/dist/client/v2-e2ee.d.ts.map +1 -1
  41. package/dist/client/v2-e2ee.js +771 -471
  42. package/dist/client/v2-e2ee.js.map +1 -1
  43. package/dist/client.d.ts.map +1 -1
  44. package/dist/client.js +13 -2
  45. package/dist/client.js.map +1 -1
  46. package/dist/facades.d.ts +2 -0
  47. package/dist/facades.d.ts.map +1 -1
  48. package/dist/facades.js +6 -0
  49. package/dist/facades.js.map +1 -1
  50. package/dist/index.d.ts +3 -2
  51. package/dist/index.d.ts.map +1 -1
  52. package/dist/index.js +1 -0
  53. package/dist/index.js.map +1 -1
  54. package/dist/ownership-proof.d.ts +30 -0
  55. package/dist/ownership-proof.d.ts.map +1 -0
  56. package/dist/ownership-proof.js +97 -0
  57. package/dist/ownership-proof.js.map +1 -0
  58. package/dist/version.d.ts +1 -1
  59. package/dist/version.js +1 -1
  60. package/package.json +5 -5
package/dist/bundle.js CHANGED
@@ -460,7 +460,7 @@ var init_indexeddb_store = __esm({
460
460
  });
461
461
 
462
462
  // src/version.ts
463
- var VERSION = "0.5.17";
463
+ var VERSION = "0.5.18";
464
464
 
465
465
  // src/types.ts
466
466
  var ConnectionState = /* @__PURE__ */ ((ConnectionState2) => {
@@ -6723,6 +6723,16 @@ var MessageDeliveryEngine = class {
6723
6723
  if (typeof senderDevice !== "string" || !senderDevice.trim() || !String(client._deviceId ?? "").trim()) return false;
6724
6724
  return senderDevice.trim() === String(client._deviceId).trim();
6725
6725
  }
6726
+ isSelfSentP2PMessage(message) {
6727
+ if (!isJsonObject(message)) return false;
6728
+ const client = this.runtime.client;
6729
+ if (String(message.direction ?? "").trim() !== "outbound_sync") return false;
6730
+ const selfAid = String(client._aid ?? "").trim().toLowerCase();
6731
+ const senderAid = String(message.sender_aid ?? message.from_aid ?? message.from ?? "").trim().toLowerCase();
6732
+ if (!selfAid || senderAid !== selfAid) return false;
6733
+ const senderDevice = Object.prototype.hasOwnProperty.call(message, "sender_device_id") ? message.sender_device_id : message.from_device_id;
6734
+ return typeof senderDevice === "string" && Boolean(senderDevice.trim()) && Boolean(String(client._deviceId ?? "").trim()) && senderDevice.trim() === String(client._deviceId).trim();
6735
+ }
6726
6736
  strictTargetString(source, key) {
6727
6737
  if (!Object.prototype.hasOwnProperty.call(source, key)) {
6728
6738
  return { present: false, valid: false, value: "" };
@@ -6828,14 +6838,18 @@ var MessageDeliveryEngine = class {
6828
6838
  await this.processV2P2PNotification(msg, "v1", inlineGeneration);
6829
6839
  return;
6830
6840
  }
6841
+ const encryptedPush = client._isEncryptedPushMessage(msg);
6831
6842
  if (client._aid && legacyPushSeq > 0) {
6832
6843
  const ns2 = `p2p:${client._aid}`;
6833
6844
  client._seqTracker.updateMaxSeen(ns2, legacyPushSeq);
6834
- if (client._sessionOptions?.background_sync !== false) client._safeAsync(this.fillP2pGap());
6845
+ if (encryptedPush) {
6846
+ if (client._sessionOptions?.background_sync !== false) client._safeAsync(this.fillP2pGap());
6847
+ return;
6848
+ }
6849
+ await this.publishPulledMessage("message.received", ns2, legacyPushSeq, msg, true, "push");
6835
6850
  return;
6836
6851
  }
6837
6852
  const seq2 = msg.seq;
6838
- const encryptedPush = client._isEncryptedPushMessage(msg);
6839
6853
  const ns = client._aid ? `p2p:${client._aid}` : "";
6840
6854
  const seqNum = positiveSafeSequenceHint(seq2);
6841
6855
  if (ns && seqNum > 0) {
@@ -6848,7 +6862,7 @@ var MessageDeliveryEngine = class {
6848
6862
  }
6849
6863
  if (ns && seq2 !== void 0 && seq2 !== null) {
6850
6864
  await this.publishOrderedMessage("message.received", ns, seq2, msg);
6851
- } else {
6865
+ } else if (!this.isSelfSentP2PMessage(msg)) {
6852
6866
  await client._publishAppEvent("message.received", msg, "push");
6853
6867
  }
6854
6868
  } catch (exc) {
@@ -6907,9 +6921,18 @@ var MessageDeliveryEngine = class {
6907
6921
  if (groupId && legacyPushSeq > 0) {
6908
6922
  const ns2 = `group:${groupId}`;
6909
6923
  client._seqTracker.updateMaxSeen(ns2, legacyPushSeq);
6910
- if (client._sessionOptions?.background_sync !== false) client._safeAsync(this.fillGroupGap(groupId));
6924
+ if (client._sessionOptions?.background_sync === false) return;
6925
+ const encryptedPush2 = client._isEncryptedPushMessage(msg);
6926
+ if (encryptedPush2) {
6927
+ client._safeAsync(this.fillGroupGap(groupId));
6928
+ return;
6929
+ }
6930
+ if (!this.isSelfSentGroupMessage(msg)) {
6931
+ await this.publishPulledMessage("group.message_created", ns2, legacyPushSeq, msg, true, "push");
6932
+ }
6911
6933
  return;
6912
6934
  }
6935
+ const encryptedPush = client._isEncryptedPushMessage(msg);
6913
6936
  if (payload === void 0 || payload === null || typeof payload === "object" && Object.keys(payload).length === 0) {
6914
6937
  if (client._v2Session) await this.autoPullGroupMessages(msg);
6915
6938
  else if (groupId && seq2 !== void 0 && seq2 !== null) {
@@ -6923,7 +6946,6 @@ var MessageDeliveryEngine = class {
6923
6946
  } else await client._publishAppEvent("group.message_created", msg, "push");
6924
6947
  return;
6925
6948
  }
6926
- const encryptedPush = client._isEncryptedPushMessage(msg);
6927
6949
  const ns = groupId ? `group:${groupId}` : "";
6928
6950
  const seqNum = positiveSafeSequenceHint(seq2);
6929
6951
  if (ns && seqNum > 0) {
@@ -7581,7 +7603,8 @@ var MessageDeliveryEngine = class {
7581
7603
  const tracker = client._seqTracker;
7582
7604
  const snapshot = typeof tracker.snapshotNamespace === "function" ? tracker.snapshotNamespace(ns) : null;
7583
7605
  try {
7584
- const published = await this.publishPulledMessage(event, ns, seq2, payload, true, "inline_push");
7606
+ const selfSync = event === "message.received" && this.isSelfSentP2PMessage(payload) || event === "group.message_created" && this.isSelfSentGroupMessage(payload);
7607
+ const published = selfSync ? (this.markPublishedSeq(ns, seq2), true) : await this.publishPulledMessage(event, ns, seq2, payload, true, "inline_push");
7585
7608
  if (!this.isInlineGenerationCurrent(generation)) return false;
7586
7609
  if (!published) return false;
7587
7610
  const needsPull = Boolean(tracker.onMessageSeq(ns, seq2));
@@ -8448,6 +8471,11 @@ var MessageDeliveryEngine = class {
8448
8471
  if (queue2 && queue2.size === 0) client._pendingOrderedMsgs.delete(ns);
8449
8472
  return false;
8450
8473
  }
8474
+ if (event === "message.received" && this.isSelfSentP2PMessage(payload)) {
8475
+ this.markPublishedSeq(ns, seqNum);
8476
+ client._clientLog.debug(`publish ordered skipped current-device self-sync: ns=${ns}, seq=${seqNum}`);
8477
+ return false;
8478
+ }
8451
8479
  const contig = client._seqTracker.getContiguousSeq(ns);
8452
8480
  if (seqNum > contig) {
8453
8481
  this.enqueueOrderedMessage(ns, event, seqNum, payload, source);
@@ -8494,6 +8522,11 @@ var MessageDeliveryEngine = class {
8494
8522
  if (queue && queue.size === 0) client._pendingOrderedMsgs.delete(ns);
8495
8523
  return false;
8496
8524
  }
8525
+ if (event === "message.received" && this.isSelfSentP2PMessage(payload)) {
8526
+ this.markPublishedSeq(ns, seqNum);
8527
+ client._clientLog.debug(`publish pulled skipped current-device self-sync: ns=${ns}, seq=${seqNum}`);
8528
+ return source === "inline_push";
8529
+ }
8497
8530
  await this.drainOrderedMessages(ns, seqNum, false, persist, batch, source);
8498
8531
  if (pullScoped) this.ensurePullOperationCurrent(ns);
8499
8532
  queue?.delete(seqNum);
@@ -9190,8 +9223,11 @@ async function certMatchesFingerprint(certPem, fingerprint) {
9190
9223
  const expected = normalizeFingerprintHex(fingerprint);
9191
9224
  if (!expected) return false;
9192
9225
  const derHex = (await certificateSha256Fingerprint(certPem)).slice("sha256:".length);
9193
- if (expected.length === 16) return derHex.slice(0, 16) === expected;
9194
- return derHex === expected;
9226
+ const spkiHex = (await publicKeyFingerprint(certPem)).slice("sha256:".length);
9227
+ if (expected.length === 16) {
9228
+ return derHex.slice(0, 16) === expected || spkiHex.slice(0, 16) === expected;
9229
+ }
9230
+ return derHex === expected || spkiHex === expected;
9195
9231
  }
9196
9232
  async function publicKeyMatchesFingerprint(certPem, fingerprint) {
9197
9233
  const expected = normalizeFingerprintHex(fingerprint);
@@ -15648,6 +15684,241 @@ function parseDocument(source, options = {}) {
15648
15684
  return doc;
15649
15685
  }
15650
15686
 
15687
+ // src/v2/crypto/canonical.ts
15688
+ var encoder = new TextEncoder();
15689
+ var MAX_SAFE_JSON_INTEGER = 9007199254740991;
15690
+ function canonicalJson(obj) {
15691
+ return encoder.encode(serialize(obj));
15692
+ }
15693
+ function serialize(obj) {
15694
+ if (obj === null) return "null";
15695
+ if (obj === true) return "true";
15696
+ if (obj === false) return "false";
15697
+ if (typeof obj === "number") {
15698
+ return serializeNumber(obj);
15699
+ }
15700
+ if (typeof obj === "string") {
15701
+ return serializeString(obj);
15702
+ }
15703
+ if (Array.isArray(obj)) {
15704
+ return serializeArray(obj);
15705
+ }
15706
+ if (typeof obj === "object") {
15707
+ return serializeObject(obj);
15708
+ }
15709
+ throw new TypeError(`canonicalJson: unsupported type ${typeof obj}`);
15710
+ }
15711
+ function serializeNumber(n) {
15712
+ if (!isFinite(n)) {
15713
+ throw new RangeError("canonicalJson: Infinity and NaN not allowed");
15714
+ }
15715
+ if (Object.is(n, -0)) return "0";
15716
+ if (Number.isInteger(n)) {
15717
+ if (Math.abs(n) > MAX_SAFE_JSON_INTEGER) {
15718
+ throw new RangeError(`canonicalJson: integer outside safe range ${n}`);
15719
+ }
15720
+ return String(n);
15721
+ }
15722
+ return expandExponent(String(n));
15723
+ }
15724
+ function expandExponent(s) {
15725
+ if (!/[eE]/.test(s)) return s;
15726
+ const match = /^(-?)(\d+)(?:\.(\d+))?[eE]([+-]?\d+)$/.exec(s);
15727
+ if (!match) {
15728
+ throw new TypeError(`canonicalJson: invalid number ${s}`);
15729
+ }
15730
+ const sign = match[1] ?? "";
15731
+ const intPart = match[2] ?? "";
15732
+ const fracPart = match[3] ?? "";
15733
+ const exp = Number(match[4]);
15734
+ const digits = intPart + fracPart;
15735
+ const point = intPart.length + exp;
15736
+ if (point <= 0) {
15737
+ return `${sign}0.${"0".repeat(-point)}${digits}`;
15738
+ }
15739
+ if (point >= digits.length) {
15740
+ return `${sign}${digits}${"0".repeat(point - digits.length)}`;
15741
+ }
15742
+ return `${sign}${digits.slice(0, point)}.${digits.slice(point)}`;
15743
+ }
15744
+ function serializeString(s) {
15745
+ let result = '"';
15746
+ for (let i = 0; i < s.length; i++) {
15747
+ const ch = s[i];
15748
+ const code = s.charCodeAt(i);
15749
+ if (ch === '"') {
15750
+ result += '\\"';
15751
+ } else if (ch === "\\") {
15752
+ result += "\\\\";
15753
+ } else if (ch === "\b") {
15754
+ result += "\\b";
15755
+ } else if (ch === "\f") {
15756
+ result += "\\f";
15757
+ } else if (ch === "\n") {
15758
+ result += "\\n";
15759
+ } else if (ch === "\r") {
15760
+ result += "\\r";
15761
+ } else if (ch === " ") {
15762
+ result += "\\t";
15763
+ } else if (code < 32) {
15764
+ result += "\\u" + code.toString(16).padStart(4, "0");
15765
+ } else {
15766
+ result += ch;
15767
+ }
15768
+ }
15769
+ result += '"';
15770
+ return result;
15771
+ }
15772
+ function serializeArray(arr) {
15773
+ const items = arr.map((item) => serialize(item));
15774
+ return "[" + items.join(",") + "]";
15775
+ }
15776
+ function serializeObject(obj) {
15777
+ const sortedKeys = Object.keys(obj).sort(compareCodePoints);
15778
+ const pairs2 = sortedKeys.map(
15779
+ (key) => serializeString(key) + ":" + serialize(obj[key])
15780
+ );
15781
+ return "{" + pairs2.join(",") + "}";
15782
+ }
15783
+ function compareCodePoints(a, b) {
15784
+ const ac = Array.from(a);
15785
+ const bc = Array.from(b);
15786
+ const n = Math.min(ac.length, bc.length);
15787
+ for (let i = 0; i < n; i++) {
15788
+ const av = ac[i].codePointAt(0) ?? 0;
15789
+ const bv = bc[i].codePointAt(0) ?? 0;
15790
+ if (av !== bv) return av - bv;
15791
+ }
15792
+ return ac.length - bc.length;
15793
+ }
15794
+
15795
+ // src/ownership-proof.ts
15796
+ var OWNERSHIP_RELATION_DOMAIN = "AUN-AID-OWNER-RELATION-V1;";
15797
+ var PROOF_ID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
15798
+ var FINGERPRINT_RE = /^sha256:[0-9a-f]{64}$/i;
15799
+ function sha256Hex(input) {
15800
+ const K = [1116352408, 1899447441, 3049323471, 3921009573, 961987163, 1508970993, 2453635748, 2870763221, 3624381080, 310598401, 607225278, 1426881987, 1925078388, 2162078206, 2614888103, 3248222580, 3835390401, 4022224774, 264347078, 604807628, 770255983, 1249150122, 1555081692, 1996064986, 2554220882, 2821834349, 2952996808, 3210313671, 3336571891, 3584528711, 113926993, 338241895, 666307205, 773529912, 1294757372, 1396182291, 1695183700, 1986661051, 2177026350, 2456956037, 2730485921, 2820302411, 3259730800, 3345764771, 3516065817, 3600352804, 4094571909, 275423344, 430227734, 506948616, 659060556, 883997877, 958139571, 1322822218, 1537002063, 1747873779, 1955562222, 2024104815, 2227730452, 2361852424, 2428436474, 2756734187, 3204031479, 3329325298];
15801
+ const bytes = new Uint8Array(input.length + 1 + ((55 - input.length) % 64 + 64) % 64 + 8);
15802
+ bytes.set(input);
15803
+ bytes[input.length] = 128;
15804
+ const bits = input.length * 8;
15805
+ new DataView(bytes.buffer).setUint32(bytes.length - 4, bits >>> 0, false);
15806
+ new DataView(bytes.buffer).setUint32(bytes.length - 8, Math.floor(bits / 4294967296), false);
15807
+ let h = [1779033703, 3144134277, 1013904242, 2773480762, 1359893119, 2600822924, 528734635, 1541459225];
15808
+ const rotr2 = (x, n) => x >>> n | x << 32 - n;
15809
+ for (let o = 0; o < bytes.length; o += 64) {
15810
+ const w = new Uint32Array(64);
15811
+ for (let i = 0; i < 16; i++) w[i] = new DataView(bytes.buffer).getUint32(o + i * 4, false);
15812
+ for (let i = 16; i < 64; i++) {
15813
+ const x = w[i - 15], y = w[i - 2];
15814
+ w[i] = w[i - 16] + (rotr2(x, 7) ^ rotr2(x, 18) ^ x >>> 3) + w[i - 7] + (rotr2(y, 17) ^ rotr2(y, 19) ^ y >>> 10) >>> 0;
15815
+ }
15816
+ let [a, b, c, d, e, f, g, hh] = h;
15817
+ for (let i = 0; i < 64; i++) {
15818
+ const s1 = rotr2(e, 6) ^ rotr2(e, 11) ^ rotr2(e, 25), ch = e & f ^ ~e & g, t1 = hh + s1 + ch + K[i] + w[i] >>> 0;
15819
+ const s0 = rotr2(a, 2) ^ rotr2(a, 13) ^ rotr2(a, 22), maj = a & b ^ a & c ^ b & c, t2 = s0 + maj >>> 0;
15820
+ [hh, g, f, e, d, c, b, a] = [g, f, e, d + t1 >>> 0, c, b, a, t1 + t2 >>> 0];
15821
+ }
15822
+ h = h.map((v, i) => v + [a, b, c, d, e, f, g, hh][i] >>> 0);
15823
+ }
15824
+ return h.map((v) => v.toString(16).padStart(8, "0")).join("");
15825
+ }
15826
+ var OwnershipStatement = class _OwnershipStatement {
15827
+ constructor(proofId, subjectAid, ownerAid) {
15828
+ __publicField(this, "proofId");
15829
+ __publicField(this, "subjectAid");
15830
+ __publicField(this, "ownerAid");
15831
+ __publicField(this, "canonicalFact");
15832
+ __publicField(this, "canonicalStatement");
15833
+ __publicField(this, "factHash");
15834
+ __publicField(this, "statementHash");
15835
+ this.proofId = proofId;
15836
+ this.subjectAid = subjectAid;
15837
+ this.ownerAid = ownerAid;
15838
+ this.canonicalFact = canonicalJson({ subject_aid: subjectAid, owner_aid: ownerAid });
15839
+ this.canonicalStatement = canonicalJson({ proof_id: proofId, subject_aid: subjectAid, owner_aid: ownerAid });
15840
+ const signed = new Uint8Array(new TextEncoder().encode(OWNERSHIP_RELATION_DOMAIN).length + this.canonicalStatement.length);
15841
+ signed.set(new TextEncoder().encode(OWNERSHIP_RELATION_DOMAIN));
15842
+ signed.set(this.canonicalStatement, new TextEncoder().encode(OWNERSHIP_RELATION_DOMAIN).length);
15843
+ this.factHash = sha256Hex(this.canonicalFact);
15844
+ this.statementHash = sha256Hex(signed);
15845
+ }
15846
+ static create(proofId, subjectAid, ownerAid) {
15847
+ const id = String(proofId ?? "").trim();
15848
+ if (!PROOF_ID_RE.test(id)) throw new ValidationError("invalid proof_id");
15849
+ let s, o;
15850
+ try {
15851
+ s = validateAIDFormat(subjectAid, "subject_aid");
15852
+ o = validateAIDFormat(ownerAid, "owner_aid");
15853
+ } catch (e) {
15854
+ throw new ValidationError(String(e instanceof Error ? e.message : e));
15855
+ }
15856
+ return new _OwnershipStatement(id, s, o);
15857
+ }
15858
+ get signingBytes() {
15859
+ const p = new TextEncoder().encode(OWNERSHIP_RELATION_DOMAIN), r = new Uint8Array(p.length + this.canonicalStatement.length);
15860
+ r.set(p);
15861
+ r.set(this.canonicalStatement, p.length);
15862
+ return r;
15863
+ }
15864
+ };
15865
+ function validateOwnershipFrontmatter(frontmatter2, expectedAid) {
15866
+ const op = Object.prototype.hasOwnProperty.call(frontmatter2, "owner"), pp = Object.prototype.hasOwnProperty.call(frontmatter2, "ownership_proof");
15867
+ if (op !== pp) throw new ValidationError("owner and ownership_proof must appear together");
15868
+ const type = String(frontmatter2.type ?? "").trim().toLowerCase();
15869
+ if (type === "human" && (op || pp)) throw new ValidationError("human AID cannot specify owner");
15870
+ if (!op) return;
15871
+ const aid = validateAIDFormat(frontmatter2.aid, "aid"), owner = validateAIDFormat(frontmatter2.owner, "owner");
15872
+ if (String(frontmatter2.aid).trim() !== aid || String(frontmatter2.owner).trim() !== owner) throw new ValidationError("owner and subject AID must be lowercase");
15873
+ if (expectedAid && aid !== expectedAid.trim().toLowerCase()) throw new ValidationError("ownership subject AID does not match expected AID");
15874
+ if (!frontmatter2.ownership_proof || typeof frontmatter2.ownership_proof !== "object" || Array.isArray(frontmatter2.ownership_proof)) throw new ValidationError("ownership_proof must be an object");
15875
+ const raw = frontmatter2.ownership_proof, allowed = /* @__PURE__ */ new Set(["proof_id", "subject_aid", "owner_aid", "fact_hash", "statement_hash", "subject_signature", "owner_signature"]);
15876
+ if (Object.keys(raw).some((k) => !allowed.has(k))) throw new ValidationError("ownership_proof contains unknown fields");
15877
+ const statement = OwnershipStatement.create(String(raw.proof_id ?? ""), String(raw.subject_aid ?? ""), String(raw.owner_aid ?? ""));
15878
+ if (statement.subjectAid !== aid || statement.ownerAid !== owner) throw new ValidationError("ownership_proof does not match agent.md owner");
15879
+ if (raw.fact_hash !== void 0 && String(raw.fact_hash).toLowerCase() !== statement.factHash) throw new ValidationError("fact_hash mismatch");
15880
+ if (raw.statement_hash !== void 0 && String(raw.statement_hash).toLowerCase() !== statement.statementHash) throw new ValidationError("statement_hash mismatch");
15881
+ if (!validSignature(raw.subject_signature)) throw new ValidationError("subject_signature is invalid");
15882
+ if (aid === owner) {
15883
+ if (raw.owner_signature !== void 0) throw new ValidationError("self-owner proof must not contain owner_signature");
15884
+ } else if (!raw.owner_signature || !validSignature(raw.owner_signature)) throw new ValidationError("owner_signature is required");
15885
+ }
15886
+ function validateOwnershipProof(proof, expectedSubject, expectedOwner) {
15887
+ let s, o;
15888
+ try {
15889
+ s = validateAIDFormat(expectedSubject, "subject_aid");
15890
+ o = validateAIDFormat(expectedOwner, "owner_aid");
15891
+ } catch (e) {
15892
+ throw new ValidationError(String(e instanceof Error ? e.message : e));
15893
+ }
15894
+ if (proof.statement.subjectAid !== s || proof.statement.ownerAid !== o) throw new ValidationError("ownership proof statement does not match owner or subject");
15895
+ if (proof.fact_hash && proof.fact_hash.toLowerCase() !== proof.statement.factHash) throw new ValidationError("fact_hash mismatch");
15896
+ if (proof.statement_hash && proof.statement_hash.toLowerCase() !== proof.statement.statementHash) throw new ValidationError("statement_hash mismatch");
15897
+ if (!validSignature(proof.subject_signature)) throw new ValidationError("subject_signature is invalid");
15898
+ if (s === o) {
15899
+ if (proof.owner_signature) throw new ValidationError("self-owner proof must not contain owner_signature");
15900
+ } else if (!proof.owner_signature || !validSignature(proof.owner_signature)) throw new ValidationError("owner_signature is required");
15901
+ }
15902
+ function validSignature(s) {
15903
+ return !!s && FINGERPRINT_RE.test(String(s?.cert_fingerprint ?? "").trim()) && isStrictBase64(String(s?.signature ?? ""));
15904
+ }
15905
+ async function signOwnershipStatement(aid, statement) {
15906
+ const sig = await aid.sign(statement.signingBytes);
15907
+ if (!sig.ok || !sig.data) throw new ValidationError(sig.error?.message ?? "ownership signature failed");
15908
+ if (!FINGERPRINT_RE.test(aid.certFingerprint)) throw new ValidationError("certificate fingerprint is unavailable");
15909
+ return { cert_fingerprint: aid.certFingerprint, signature: sig.data.signature };
15910
+ }
15911
+ async function verifyOwnershipSignature(certPem, statement, signature) {
15912
+ if (!validSignature(signature)) return false;
15913
+ if ((await certificateSha256Fingerprint(certPem)).toLowerCase() !== signature.cert_fingerprint.toLowerCase()) return false;
15914
+ return verifySignatureWithCert(certPem, signature.signature, new TextEncoder().encode(new TextDecoder().decode(statement.signingBytes)));
15915
+ }
15916
+ async function verifyOwnershipProof(proof, subjectCertPem, ownerCertPem) {
15917
+ validateOwnershipProof(proof, proof.statement.subjectAid, proof.statement.ownerAid);
15918
+ if (!await verifyOwnershipSignature(subjectCertPem, proof.statement, proof.subject_signature)) return false;
15919
+ return proof.statement.subjectAid === proof.statement.ownerAid || !!ownerCertPem && !!proof.owner_signature && await verifyOwnershipSignature(ownerCertPem, proof.statement, proof.owner_signature);
15920
+ }
15921
+
15651
15922
  // src/agent-md-schema.ts
15652
15923
  var AID_RE = /^[A-Za-z0-9_][A-Za-z0-9_-]*(?:\.[A-Za-z0-9_][A-Za-z0-9_-]*)+$/;
15653
15924
  var SEMVER_RE = /^\d+\.\d+\.\d+$/;
@@ -15693,6 +15964,7 @@ function validateAgentMdDocument(content, opts = {}) {
15693
15964
  if (Array.from(meta.avatar).length > 256) throw new ValidationError("agent.md avatar exceeds 256 characters");
15694
15965
  }
15695
15966
  if (meta.tags !== void 0 && (!Array.isArray(meta.tags) || meta.tags.some((item) => typeof item !== "string"))) throw new ValidationError("agent.md tags must be an array of strings");
15967
+ validateOwnershipFrontmatter(meta, opts.expectedAid);
15696
15968
  const fields = parsed.fields;
15697
15969
  if (fields) {
15698
15970
  if (Object.keys(fields).some((key) => !["cert_fingerprint", "public_key_fingerprint", "timestamp", "signature"].includes(key))) throw new ValidationError("agent.md signature contains unknown fields");
@@ -15789,6 +16061,8 @@ var AID = class _AID {
15789
16061
  return this._privateKeyValid;
15790
16062
  }
15791
16063
  async sign(payload) {
16064
+ const now = Date.now();
16065
+ if (!this._certValid || now < this.certNotBefore.getTime() || now > this.certNotAfter.getTime()) return resultErr(CERT_NOT_VALID, "certificate is not valid");
15792
16066
  if (!this._privateKeyValid || !this.privateKeyPem) return resultErr(PRIVATE_KEY_NOT_VALID, "private key is not valid");
15793
16067
  try {
15794
16068
  const data = typeof payload === "string" ? new TextEncoder().encode(payload) : payload;
@@ -15797,7 +16071,8 @@ var AID = class _AID {
15797
16071
  return resultErr(SIGNATURE_OPERATION_ERROR, String(exc), exc);
15798
16072
  }
15799
16073
  }
15800
- async verify(payload, signature) {
16074
+ async verify(payload, signature, context) {
16075
+ if (context) return this.verifyWithContext(payload, signature, context);
15801
16076
  if (!this._certValid) return resultErr(CERT_NOT_VALID, "certificate is not valid");
15802
16077
  try {
15803
16078
  const data = typeof payload === "string" ? new TextEncoder().encode(payload) : payload;
@@ -15807,6 +16082,8 @@ var AID = class _AID {
15807
16082
  }
15808
16083
  }
15809
16084
  async signAgentMd(content) {
16085
+ const now = Date.now();
16086
+ if (!this._certValid || now < this.certNotBefore.getTime() || now > this.certNotAfter.getTime()) return resultErr(CERT_NOT_VALID, "certificate is not valid");
15810
16087
  if (!this._privateKeyValid || !this.privateKeyPem) return resultErr(PRIVATE_KEY_NOT_VALID, "private key is not valid");
15811
16088
  try {
15812
16089
  const parsed = parseAgentMdTailSignature(String(content ?? ""));
@@ -15828,6 +16105,40 @@ var AID = class _AID {
15828
16105
  return resultErr(SIGNATURE_OPERATION_ERROR, String(exc), exc);
15829
16106
  }
15830
16107
  }
16108
+ async verifyWithContext(payload, signature, context) {
16109
+ try {
16110
+ const expectedAid = String(context.expectedAid ?? context.expected_aid ?? context.aid ?? "").trim();
16111
+ if (!expectedAid) return resultErr(VERIFICATION_OPERATION_ERROR, "expected AID is required");
16112
+ if (expectedAid.toLowerCase() !== this.aid.trim().toLowerCase() || expectedAid.toLowerCase() !== this.certSubject.trim().toLowerCase()) {
16113
+ return resultErr(VERIFICATION_OPERATION_ERROR, "certificate subject does not match AID");
16114
+ }
16115
+ const expectedFingerprint = String(context.certFingerprint ?? context.cert_fingerprint ?? "").trim().toLowerCase().replace(/^sha256:/, "").replace(/:/g, "");
16116
+ const actualFingerprint = this.certFingerprint.trim().toLowerCase().replace(/^sha256:/, "").replace(/:/g, "");
16117
+ if (!/^[0-9a-f]{64}$/.test(expectedFingerprint) || expectedFingerprint !== actualFingerprint) {
16118
+ return resultErr(VERIFICATION_OPERATION_ERROR, "certificate fingerprint mismatch");
16119
+ }
16120
+ const timestampValue = context.signatureTimestamp ?? context.signature_timestamp ?? context.timestamp;
16121
+ if (typeof timestampValue !== "number" || !Number.isFinite(timestampValue) || !Number.isInteger(timestampValue)) {
16122
+ return resultErr(VERIFICATION_OPERATION_ERROR, "signature timestamp is invalid");
16123
+ }
16124
+ const notBefore = this.certNotBefore.getTime() / 1e3;
16125
+ const notAfter = this.certNotAfter.getTime() / 1e3;
16126
+ if (timestampValue < notBefore || timestampValue > notAfter) {
16127
+ return resultErr(VERIFICATION_OPERATION_ERROR, "signature timestamp is outside certificate validity");
16128
+ }
16129
+ const nowValue = context.now instanceof Date ? context.now.getTime() / 1e3 : context.now;
16130
+ const now = nowValue === void 0 ? Date.now() / 1e3 : Number(nowValue);
16131
+ if (!Number.isFinite(now)) return resultErr(VERIFICATION_OPERATION_ERROR, "verification time is invalid");
16132
+ if ((context.requireCurrentValidity ?? context.require_current_validity ?? false) && (now < notBefore || now > notAfter)) {
16133
+ return resultErr(CERT_NOT_VALID, "certificate is expired or not yet valid");
16134
+ }
16135
+ if (!isStrictBase64(signature)) return resultErr(VERIFICATION_OPERATION_ERROR, "invalid base64 signature");
16136
+ const data = typeof payload === "string" ? new TextEncoder().encode(payload) : payload;
16137
+ return resultOk({ valid: await verifySignatureWithCert(this.certPem, signature, data) });
16138
+ } catch (exc) {
16139
+ return resultErr(VERIFICATION_OPERATION_ERROR, String(exc), exc);
16140
+ }
16141
+ }
15831
16142
  async verifyAgentMd(content) {
15832
16143
  try {
15833
16144
  const rawContent = String(content ?? "");
@@ -16577,6 +16888,13 @@ var RpcPipeline = class {
16577
16888
  if (!currentAid?.privateKeyPem) return;
16578
16889
  try {
16579
16890
  const aid = currentAid.aid;
16891
+ const certPem = currentAid.certPem ?? "";
16892
+ if (!certPem) throw new Error("\u7B7E\u540D\u8BC1\u4E66\u4E0D\u53EF\u7528");
16893
+ const certMeta = parseCertMetadata(certPem);
16894
+ const now = Date.now();
16895
+ if (now < certMeta.notBefore.getTime() || now > certMeta.notAfter.getTime()) {
16896
+ throw new Error("\u7B7E\u540D\u8BC1\u4E66\u5DF2\u8FC7\u671F\u6216\u5C1A\u672A\u751F\u6548");
16897
+ }
16580
16898
  const ts = String(Math.floor(Date.now() / 1e3));
16581
16899
  const paramsForHash = {};
16582
16900
  for (const [k, v] of Object.entries(params2)) {
@@ -16599,7 +16917,6 @@ var RpcPipeline = class {
16599
16917
  );
16600
16918
  const sigDer = p1363ToDer(new Uint8Array(sigP1363));
16601
16919
  let certFingerprint = "";
16602
- const certPem = currentAid.certPem;
16603
16920
  if (certPem) {
16604
16921
  certFingerprint = await signingCertFingerprint(certPem);
16605
16922
  }
@@ -17951,7 +18268,7 @@ function unmountFromAny(input, fallback) {
17951
18268
  }
17952
18269
 
17953
18270
  // src/storage/vfs.ts
17954
- async function sha256Hex(data) {
18271
+ async function sha256Hex2(data) {
17955
18272
  const copy = new Uint8Array(data.byteLength);
17956
18273
  copy.set(data);
17957
18274
  const subtle = globalThis.crypto?.subtle;
@@ -18072,7 +18389,7 @@ var StorageVFS = class {
18072
18389
  const owner = this.owner(options.owner);
18073
18390
  const bucket = options.bucket ?? "default";
18074
18391
  const objectKey = pathToKey(path);
18075
- const sha2566 = await sha256Hex(data);
18392
+ const sha2566 = await sha256Hex2(data);
18076
18393
  const overwrite = options.overwrite ?? false;
18077
18394
  try {
18078
18395
  const check = await this.lowlevel.checkUpload({ owner, bucket, objectKey, size: data.length, sha256: sha2566 });
@@ -18178,7 +18495,7 @@ var StorageVFS = class {
18178
18495
  }
18179
18496
  async downloadFile(path, options = {}) {
18180
18497
  const { data, sha256: expectedSha } = await this.readBytesWithMetadata(path, options);
18181
- const actualSha = await sha256Hex(data);
18498
+ const actualSha = await sha256Hex2(data);
18182
18499
  const expected = expectedSha.toLowerCase();
18183
18500
  const verified = Boolean(expected) && actualSha.toLowerCase() === expected;
18184
18501
  if (!verified && expected && (options.verifyHash ?? true)) {
@@ -18531,7 +18848,7 @@ function bearerHeaders(client) {
18531
18848
  function toExactArrayBuffer(bytes) {
18532
18849
  return bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength);
18533
18850
  }
18534
- async function sha256Hex2(data) {
18851
+ async function sha256Hex3(data) {
18535
18852
  const subtle = globalThis.crypto?.subtle;
18536
18853
  if (!subtle?.digest) throw new StorageError("SHA-256 unavailable", "EUNSUPPORTED");
18537
18854
  const digest = await subtle.digest("SHA-256", toExactArrayBuffer(data));
@@ -18786,7 +19103,7 @@ var GroupFSVFS = class {
18786
19103
  }
18787
19104
  async uploadToGroup(src, dst, options) {
18788
19105
  const { data, contentType } = await this.sourceBytes(src, options);
18789
- const digest = await sha256Hex2(data);
19106
+ const digest = await sha256Hex3(data);
18790
19107
  const groupAid = options.group_aid ?? options.groupAid ?? options.dst_group_aid ?? options.dstGroupAid;
18791
19108
  const groupId = options.group_id ?? options.groupId ?? options.dst_group_id ?? options.dstGroupId;
18792
19109
  const baseParams = stripNil({
@@ -18910,7 +19227,7 @@ var GroupFSVFS = class {
18910
19227
  const data = await this.lowlevel.httpGet(downloadUrl, bearerHeaders(this.client));
18911
19228
  const expectedSha = text2(rawTicket.sha256);
18912
19229
  const verifyHash = options.verify_hash ?? options.verifyHash ?? true;
18913
- const verified = !verifyHash || !expectedSha || await sha256Hex2(data) === expectedSha.toLowerCase();
19230
+ const verified = !verifyHash || !expectedSha || await sha256Hex3(data) === expectedSha.toLowerCase();
18914
19231
  if (verifyHash && !verified) {
18915
19232
  throw new StorageConflictError("download hash verification failed", "ECONFLICT", src, ticket);
18916
19233
  }
@@ -18941,114 +19258,6 @@ var GroupFSVFS = class {
18941
19258
  }
18942
19259
  };
18943
19260
 
18944
- // src/v2/crypto/canonical.ts
18945
- var encoder = new TextEncoder();
18946
- var MAX_SAFE_JSON_INTEGER = 9007199254740991;
18947
- function canonicalJson(obj) {
18948
- return encoder.encode(serialize(obj));
18949
- }
18950
- function serialize(obj) {
18951
- if (obj === null) return "null";
18952
- if (obj === true) return "true";
18953
- if (obj === false) return "false";
18954
- if (typeof obj === "number") {
18955
- return serializeNumber(obj);
18956
- }
18957
- if (typeof obj === "string") {
18958
- return serializeString(obj);
18959
- }
18960
- if (Array.isArray(obj)) {
18961
- return serializeArray(obj);
18962
- }
18963
- if (typeof obj === "object") {
18964
- return serializeObject(obj);
18965
- }
18966
- throw new TypeError(`canonicalJson: unsupported type ${typeof obj}`);
18967
- }
18968
- function serializeNumber(n) {
18969
- if (!isFinite(n)) {
18970
- throw new RangeError("canonicalJson: Infinity and NaN not allowed");
18971
- }
18972
- if (Object.is(n, -0)) return "0";
18973
- if (Number.isInteger(n)) {
18974
- if (Math.abs(n) > MAX_SAFE_JSON_INTEGER) {
18975
- throw new RangeError(`canonicalJson: integer outside safe range ${n}`);
18976
- }
18977
- return String(n);
18978
- }
18979
- return expandExponent(String(n));
18980
- }
18981
- function expandExponent(s) {
18982
- if (!/[eE]/.test(s)) return s;
18983
- const match = /^(-?)(\d+)(?:\.(\d+))?[eE]([+-]?\d+)$/.exec(s);
18984
- if (!match) {
18985
- throw new TypeError(`canonicalJson: invalid number ${s}`);
18986
- }
18987
- const sign = match[1] ?? "";
18988
- const intPart = match[2] ?? "";
18989
- const fracPart = match[3] ?? "";
18990
- const exp = Number(match[4]);
18991
- const digits = intPart + fracPart;
18992
- const point = intPart.length + exp;
18993
- if (point <= 0) {
18994
- return `${sign}0.${"0".repeat(-point)}${digits}`;
18995
- }
18996
- if (point >= digits.length) {
18997
- return `${sign}${digits}${"0".repeat(point - digits.length)}`;
18998
- }
18999
- return `${sign}${digits.slice(0, point)}.${digits.slice(point)}`;
19000
- }
19001
- function serializeString(s) {
19002
- let result = '"';
19003
- for (let i = 0; i < s.length; i++) {
19004
- const ch = s[i];
19005
- const code = s.charCodeAt(i);
19006
- if (ch === '"') {
19007
- result += '\\"';
19008
- } else if (ch === "\\") {
19009
- result += "\\\\";
19010
- } else if (ch === "\b") {
19011
- result += "\\b";
19012
- } else if (ch === "\f") {
19013
- result += "\\f";
19014
- } else if (ch === "\n") {
19015
- result += "\\n";
19016
- } else if (ch === "\r") {
19017
- result += "\\r";
19018
- } else if (ch === " ") {
19019
- result += "\\t";
19020
- } else if (code < 32) {
19021
- result += "\\u" + code.toString(16).padStart(4, "0");
19022
- } else {
19023
- result += ch;
19024
- }
19025
- }
19026
- result += '"';
19027
- return result;
19028
- }
19029
- function serializeArray(arr) {
19030
- const items = arr.map((item) => serialize(item));
19031
- return "[" + items.join(",") + "]";
19032
- }
19033
- function serializeObject(obj) {
19034
- const sortedKeys = Object.keys(obj).sort(compareCodePoints);
19035
- const pairs2 = sortedKeys.map(
19036
- (key) => serializeString(key) + ":" + serialize(obj[key])
19037
- );
19038
- return "{" + pairs2.join(",") + "}";
19039
- }
19040
- function compareCodePoints(a, b) {
19041
- const ac = Array.from(a);
19042
- const bc = Array.from(b);
19043
- const n = Math.min(ac.length, bc.length);
19044
- for (let i = 0; i < n; i++) {
19045
- const av = ac[i].codePointAt(0) ?? 0;
19046
- const bv = bc[i].codePointAt(0) ?? 0;
19047
- if (av !== bv) return av - bv;
19048
- }
19049
- return ac.length - bc.length;
19050
- }
19051
-
19052
19261
  // src/group-index.ts
19053
19262
  var GROUP_INDEX_SCHEMA = "aun.group.index.v1";
19054
19263
  var GROUP_INDEX_KEY = "group.index";
@@ -19156,13 +19365,13 @@ var GroupIndexMetaCache = class {
19156
19365
  }
19157
19366
  };
19158
19367
  async function computeGroupIndexBodyHash(entries) {
19159
- return `sha256:${await sha256Hex3(entriesBytes(entries))}`;
19368
+ return `sha256:${await sha256Hex4(entriesBytes(entries))}`;
19160
19369
  }
19161
19370
  async function groupIndexEtag(entries) {
19162
- return `"sha256:${await sha256Hex3(entriesBytes(entries))}"`;
19371
+ return `"sha256:${await sha256Hex4(entriesBytes(entries))}"`;
19163
19372
  }
19164
19373
  async function groupIndexSettingEtag(value) {
19165
- return `"sha256:${await sha256Hex3(encode(canonicalStringify(value)))}"`;
19374
+ return `"sha256:${await sha256Hex4(encode(canonicalStringify(value)))}"`;
19166
19375
  }
19167
19376
  function groupIndexSigningPayload(meta, entries) {
19168
19377
  const metaWithoutSignature = { ...meta };
@@ -19266,7 +19475,7 @@ function canonicalStringify(value) {
19266
19475
  function encode(text3) {
19267
19476
  return new TextEncoder().encode(text3);
19268
19477
  }
19269
- async function sha256Hex3(data) {
19478
+ async function sha256Hex4(data) {
19270
19479
  const digest = await globalThis.crypto.subtle.digest("SHA-256", exactArrayBuffer(data));
19271
19480
  return bytesToHex(new Uint8Array(digest));
19272
19481
  }
@@ -19366,6 +19575,9 @@ var MessageFacade = class extends RpcFacade {
19366
19575
  send(params2) {
19367
19576
  return this.call("message.send", params2);
19368
19577
  }
19578
+ getAuditStatus(params2) {
19579
+ return this.call("message.get_audit_status", params2);
19580
+ }
19369
19581
  pull(params2) {
19370
19582
  return this.call("message.pull", params2);
19371
19583
  }
@@ -19811,6 +20023,9 @@ var GroupFacade = class extends RpcFacade {
19811
20023
  validateGroupIDFormat(params2?.group_id, "group_id");
19812
20024
  return this.call("group.send", params2);
19813
20025
  }
20026
+ getAuditStatus(params2) {
20027
+ return this.call("group.get_audit_status", params2);
20028
+ }
19814
20029
  recall(params2) {
19815
20030
  return this.call("group.recall", params2);
19816
20031
  }
@@ -23900,7 +24115,7 @@ var PEER_KEY_CACHE_TTL_MS = 60 * 60 * 1e3;
23900
24115
  var DESTROY_DELAY_MS = 7 * 24 * 60 * 60 * 1e3;
23901
24116
  var RECENT_GENERATIONS = 7;
23902
24117
  var HARD_LIMIT_MS = 180 * 24 * 60 * 60 * 1e3;
23903
- async function sha256Hex4(data) {
24118
+ async function sha256Hex5(data) {
23904
24119
  const buf = await crypto.subtle.digest("SHA-256", data.slice().buffer);
23905
24120
  const arr = new Uint8Array(buf);
23906
24121
  let hex = "";
@@ -23991,7 +24206,7 @@ var V2Session = class {
23991
24206
  }
23992
24207
  async _generateNewSPK() {
23993
24208
  const [priv, pubDer] = await generateP256Keypair();
23994
- const hex = await sha256Hex4(pubDer);
24209
+ const hex = await sha256Hex5(pubDer);
23995
24210
  const spkId = `sha256:${hex.substring(0, 16)}`;
23996
24211
  await this._store.saveSPK(this._storeDeviceId, spkId, priv, pubDer);
23997
24212
  this._spkId = spkId;
@@ -23999,7 +24214,7 @@ var V2Session = class {
23999
24214
  this._spkPubDer = pubDer;
24000
24215
  }
24001
24216
  async _ikSPKId() {
24002
- const hex = await sha256Hex4(this._ikPubDer);
24217
+ const hex = await sha256Hex5(this._ikPubDer);
24003
24218
  return `sha256:${hex.substring(0, 16)}`;
24004
24219
  }
24005
24220
  _groupKey(groupId) {
@@ -24229,7 +24444,7 @@ var V2Session = class {
24229
24444
  }
24230
24445
  }
24231
24446
  const [priv, pubDer] = await generateP256Keypair();
24232
- const hex = await sha256Hex4(pubDer);
24447
+ const hex = await sha256Hex5(pubDer);
24233
24448
  const spkId = `sha256:${hex.substring(0, 16)}`;
24234
24449
  await this._store.saveGroupSPK(this._storeDeviceId, gk, spkId, priv, pubDer);
24235
24450
  return { spkId, priv, pubDer };
@@ -24254,7 +24469,7 @@ var V2Session = class {
24254
24469
  await this.ensureKeys();
24255
24470
  const gk = this._groupKey(groupId);
24256
24471
  const [priv, pubDer] = await generateP256Keypair();
24257
- const hex = await sha256Hex4(pubDer);
24472
+ const hex = await sha256Hex5(pubDer);
24258
24473
  const spkId = `sha256:${hex.substring(0, 16)}`;
24259
24474
  await this._store.saveGroupSPK(this._storeDeviceId, gk, spkId, priv, pubDer);
24260
24475
  await this._publishGroupSPK(gk, spkId, pubDer, callFn);
@@ -24329,6 +24544,46 @@ var V2_GROUP_STALE_BOOTSTRAP_CODE = -33054;
24329
24544
  var V2_PULL_DEFAULT_PAGE_LIMIT = 50;
24330
24545
  var V2_PULL_MAX_PAGE_LIMIT = 50;
24331
24546
  var V2_SENDER_IK_SYNC_FETCH_TIMEOUT_MS = 3e3;
24547
+ var V2_PULL_PIPELINE_QUEUE_CAPACITY = 100;
24548
+ var V2PullPageQueue = class {
24549
+ constructor(capacity) {
24550
+ this.capacity = capacity;
24551
+ __publicField(this, "items", []);
24552
+ __publicField(this, "takers", []);
24553
+ __publicField(this, "putters", []);
24554
+ __publicField(this, "queuedWeight", 0);
24555
+ __publicField(this, "closed", false);
24556
+ }
24557
+ async put(item, weight = 1) {
24558
+ const itemWeight = Math.max(1, Math.trunc(weight));
24559
+ while (!this.closed && this.items.length > 0 && this.queuedWeight + itemWeight > this.capacity) {
24560
+ await new Promise((resolve) => this.putters.push(resolve));
24561
+ }
24562
+ if (this.closed) return false;
24563
+ const taker = this.takers.shift();
24564
+ if (taker) taker(item);
24565
+ else {
24566
+ this.items.push(item);
24567
+ this.queuedWeight += itemWeight;
24568
+ }
24569
+ return true;
24570
+ }
24571
+ async take() {
24572
+ const item = this.items.shift();
24573
+ if (item !== void 0) {
24574
+ this.queuedWeight = Math.max(0, this.queuedWeight - Math.max(1, Math.trunc(Number(item.weight ?? 1))));
24575
+ this.putters.shift()?.();
24576
+ return item;
24577
+ }
24578
+ if (this.closed) return void 0;
24579
+ return await new Promise((resolve) => this.takers.push(resolve));
24580
+ }
24581
+ close() {
24582
+ this.closed = true;
24583
+ for (const resolve of this.takers.splice(0)) resolve(void 0);
24584
+ for (const resolve of this.putters.splice(0)) resolve();
24585
+ }
24586
+ };
24332
24587
  var V2_SESSION_INIT_RETRY_LIMIT = 12;
24333
24588
  var V2_SESSION_INIT_RETRY_BASE_MS = 1e3;
24334
24589
  var V2_SESSION_INIT_RETRY_MAX_MS = 5e3;
@@ -25301,6 +25556,9 @@ var V2E2EECoordinator = class {
25301
25556
  payload,
25302
25557
  encrypted: false
25303
25558
  };
25559
+ if (msg.sender_device_id !== void 0) plaintext.sender_device_id = msg.sender_device_id;
25560
+ const direction = String(msg.direction ?? "").trim();
25561
+ if (direction) plaintext.direction = direction;
25304
25562
  attachGatewayProximity(plaintext, msg);
25305
25563
  } else if (isJsonObject(payload) && ["e2ee.p2p_encrypted", "e2ee.encrypted"].includes(payloadType)) {
25306
25564
  msg = { ...msg, envelope_json: JSON.stringify(payload) };
@@ -25314,6 +25572,9 @@ var V2E2EECoordinator = class {
25314
25572
  client._clientLog.warn(`P2P Tail \u5355\u6761\u6D88\u606F\u89E3\u5BC6\u5931\u8D25\uFF0C\u8FDE\u7EED\u6027\u8BC1\u660E\u4ECD\u4FDD\u7559: seq=${seq2}`);
25315
25573
  return null;
25316
25574
  }
25575
+ if (publish && client._delivery.isSelfSentP2PMessage?.(plaintext)) {
25576
+ return plaintext;
25577
+ }
25317
25578
  if (publish) {
25318
25579
  const event = client._delivery.p2pAppEventForMessage(plaintext);
25319
25580
  if (orderedPublish) {
@@ -25380,6 +25641,7 @@ var V2E2EECoordinator = class {
25380
25641
  payload,
25381
25642
  encrypted: false
25382
25643
  };
25644
+ if (msg.sender_device_id !== void 0) plaintext.sender_device_id = msg.sender_device_id;
25383
25645
  plaintext = normalizeGroupMentionMode(plaintext, msg, msg.envelope_json);
25384
25646
  attachGatewayProximity(plaintext, msg);
25385
25647
  }
@@ -25543,257 +25805,367 @@ var V2E2EECoordinator = class {
25543
25805
  let lastAutoAckSeq = 0;
25544
25806
  let pendingAckSeq = deferredForwardAck;
25545
25807
  const maxPages = Math.max(1, Math.min(100, Math.trunc(Number(opts?.maxPages ?? 100) || 100)));
25546
- while (pageCount < maxPages) {
25547
- pageCount += 1;
25548
- const volatileCursorBeforePage = volatileCursor;
25549
- const ackUpToSeq = pendingAckSeq;
25550
- const pendingInlineGeneration = ackUpToSeq > 0 ? client._delivery?.pendingP2PInlineAckGeneration?.(ns) ?? null : null;
25551
- let result;
25552
- try {
25553
- result = await client._callRawV2Rpc("message.v2.pull", {
25554
- after_seq: nextAfterSeq,
25555
- limit,
25556
- ...volatileCursor ? { after_volatile_cursor: volatileCursor } : {},
25557
- ...opts?.force ? { force: true } : {},
25558
- ...ackUpToSeq > 0 ? { ack_up_to_seq: ackUpToSeq } : {}
25559
- }, pullGateKey);
25560
- } catch (exc) {
25561
- const text3 = exc instanceof Error ? exc.message : String(exc);
25562
- if (!volatileCursor || !text3.toLowerCase().includes("volatile cursor epoch mismatch")) throw exc;
25563
- client._delivery.resetVolatilePullCursor();
25564
- volatileCursor = "";
25565
- pageCount -= 1;
25566
- continue;
25567
- }
25568
- if (ackUpToSeq > 0) {
25569
- const actualAckSeq = client._delivery?.resolveP2PPullAckSeq?.(result, ackUpToSeq) ?? 0;
25570
- if (actualAckSeq >= ackUpToSeq) {
25571
- if (pendingInlineGeneration !== null) {
25572
- client._delivery?.completePendingP2PInlineAckUpTo?.(ns, actualAckSeq, pendingInlineGeneration);
25808
+ let pipelineRunning = false;
25809
+ let pipelineError = null;
25810
+ const pipelineQueue = new V2PullPageQueue(V2_PULL_PIPELINE_QUEUE_CAPACITY);
25811
+ let pipelineProducer = null;
25812
+ let pipelineStopped = false;
25813
+ const startPipeline = (startAfterSeq, startVolatileCursor) => {
25814
+ if (pipelineRunning || pageCount >= maxPages || limit < V2_PULL_MAX_PAGE_LIMIT) return;
25815
+ pipelineRunning = true;
25816
+ pipelineProducer = (async () => {
25817
+ let producerAfterSeq = startAfterSeq;
25818
+ let producerVolatileCursor = startVolatileCursor;
25819
+ for (let produced = pageCount; produced < maxPages; produced += 1) {
25820
+ if (pipelineStopped) break;
25821
+ const requestedVolatileCursor = producerVolatileCursor;
25822
+ const result = await client._callRawV2Rpc("message.v2.pull", {
25823
+ after_seq: producerAfterSeq,
25824
+ limit,
25825
+ ...producerVolatileCursor ? { after_volatile_cursor: producerVolatileCursor } : {},
25826
+ ...opts?.force ? { force: true } : {}
25827
+ }, pullGateKey);
25828
+ if (pipelineStopped) break;
25829
+ const rawPageMessages = Array.isArray(result?.messages) ? result.messages : [];
25830
+ const enqueued = await pipelineQueue.put({
25831
+ result,
25832
+ afterSeq: producerAfterSeq,
25833
+ ackUpToSeq: 0,
25834
+ inlineAckGeneration: null,
25835
+ requestedVolatileCursor,
25836
+ weight: rawPageMessages.length
25837
+ }, rawPageMessages.length);
25838
+ if (!enqueued || pipelineStopped) break;
25839
+ const volatileMessages = client._delivery.volatileMessages(rawPageMessages);
25840
+ if (volatileMessages.length > 0) break;
25841
+ const messages = deduplicateForwardPageMessages(rawPageMessages, producerAfterSeq);
25842
+ const seqs = messages.map((msg) => Number(msg.seq ?? 0)).filter((seq2) => Number.isFinite(seq2) && seq2 > 0);
25843
+ const nextAfter = Math.max(contiguousPullSeq(producerAfterSeq, seqs, result.contiguous_seq), producerAfterSeq);
25844
+ const cursor = isJsonObject(result.cursor) ? result.cursor : null;
25845
+ const shouldContinue = shouldContinueForwardPage({
25846
+ rawCount: messages.length,
25847
+ nextAfterSeq: producerAfterSeq,
25848
+ nextAfter,
25849
+ limit,
25850
+ hasMoreField: Object.prototype.hasOwnProperty.call(result, "has_more"),
25851
+ hasMore: result.has_more === true,
25852
+ serverHead: safeNonNegativeSeq(cursor?.latest_seq) ?? 0,
25853
+ observedUpper: Math.max(nextAfter, Number(client._pendingP2pPullUpper?.get?.(ns) ?? 0)),
25854
+ pendingAckSeq: 0
25855
+ });
25856
+ if (!shouldContinue) break;
25857
+ producerAfterSeq = nextAfter;
25858
+ producerVolatileCursor = "";
25859
+ }
25860
+ })().catch((error) => {
25861
+ pipelineError = error;
25862
+ }).finally(() => {
25863
+ pipelineQueue.close();
25864
+ });
25865
+ };
25866
+ const stopPipeline = async () => {
25867
+ pipelineStopped = true;
25868
+ pipelineQueue.close();
25869
+ if (pipelineProducer) {
25870
+ const producer = pipelineProducer;
25871
+ pipelineProducer = null;
25872
+ await producer;
25873
+ }
25874
+ pipelineRunning = false;
25875
+ };
25876
+ try {
25877
+ while (pageCount < maxPages) {
25878
+ let pipelinePage;
25879
+ if (pipelineRunning) {
25880
+ pipelinePage = await pipelineQueue.take();
25881
+ if (!pipelinePage) {
25882
+ pipelineRunning = false;
25883
+ if (pipelineError) throw pipelineError;
25884
+ break;
25573
25885
  }
25574
- pendingAckSeq = 0;
25575
- lastAutoAckSeq = Math.max(lastAutoAckSeq, ackUpToSeq);
25576
- this.clearForwardAck(ns, ackUpToSeq);
25886
+ }
25887
+ pageCount += 1;
25888
+ const volatileCursorBeforePage = pipelinePage?.requestedVolatileCursor ?? volatileCursor;
25889
+ const ackUpToSeq = pipelinePage?.ackUpToSeq ?? pendingAckSeq;
25890
+ const pendingInlineGeneration = pipelinePage?.inlineAckGeneration ?? (ackUpToSeq > 0 ? client._delivery?.pendingP2PInlineAckGeneration?.(ns) ?? null : null);
25891
+ let result;
25892
+ if (pipelinePage) {
25893
+ nextAfterSeq = pipelinePage.afterSeq;
25894
+ result = pipelinePage.result;
25577
25895
  } else {
25578
- client._clientLog.warn(`message.v2.pull piggyback ACK below target: requested=${ackUpToSeq}, actual=${actualAckSeq}; fallback terminal ACK`);
25579
- await this.confirmForwardP2PAck(ackUpToSeq, pendingInlineGeneration ?? void 0);
25580
- pendingAckSeq = 0;
25581
- lastAutoAckSeq = Math.max(lastAutoAckSeq, ackUpToSeq);
25582
- }
25583
- }
25584
- const pageMessages = Array.isArray(result?.messages) ? result.messages : [];
25585
- const volatileMessages = client._delivery.volatileMessages(pageMessages);
25586
- if (volatileMessages.length > 0) {
25587
- decrypted.push(...await this.publishV2VolatileMessages(volatileMessages, "pull", true));
25588
- volatileCursor = client._delivery.volatilePullCursor();
25589
- }
25590
- const volatileAdvanced = Boolean(volatileCursor) && volatileCursor !== volatileCursorBeforePage;
25591
- const messages = deduplicateForwardPageMessages(pageMessages, nextAfterSeq);
25592
- const cursor = isJsonObject(result.cursor) ? result.cursor : null;
25593
- const hasMoreField = Object.prototype.hasOwnProperty.call(result, "has_more");
25594
- const hasMore = result.has_more === true;
25595
- const serverHead = safeNonNegativeSeq(cursor?.latest_seq) ?? 0;
25596
- client._clientLog.debug(`message.v2.pull page response: page=${pageCount}, raw_count=${messages.length}, stale_count=${Math.max(0, pageMessages.length - messages.length)}, has_more=${String(result.has_more ?? "")}, server_head=${serverHead}, server_ack_seq=${String(result.server_ack_seq ?? "")}`);
25597
- const seqs = messages.map((msg) => Number(msg.seq ?? 0)).filter((seq2) => Number.isFinite(seq2) && seq2 > 0);
25598
- const pageContigBefore = ns ? client._seqTracker.getContiguousSeq(ns) : 0;
25599
- const pageTrackerSnapshot = ns && typeof client._seqTracker.snapshotNamespace === "function" ? client._seqTracker.snapshotNamespace(ns) : null;
25600
- const pageMaxSeq = seqs.length > 0 ? Math.max(...seqs) : nextAfterSeq;
25601
- const pageContiguousSeq = contiguousPullSeq(
25602
- pageContigBefore,
25603
- seqs,
25604
- result.contiguous_seq
25605
- );
25606
- const deferredKeyFetches = /* @__PURE__ */ new Map();
25607
- let blockedSeq = 0;
25608
- const pageDecryptedBefore = decrypted.length;
25609
- for (const msg of messages) {
25610
- const seq2 = Number(msg.seq ?? 0);
25611
- if (!Number.isFinite(seq2) || seq2 <= 0) continue;
25612
- const version = String(msg.version ?? "v2");
25613
- if (version === "v1") {
25614
- const legacy = isJsonObject(msg.legacy_v1) ? msg.legacy_v1 : {};
25615
- const legacyPayload = legacy.payload !== void 0 ? legacy.payload : msg.payload;
25616
- const payloadType = isJsonObject(legacyPayload) ? String(legacyPayload.type ?? "").trim() : "";
25617
- if (legacyPayload !== void 0 && legacyPayload !== null && !["e2ee.encrypted", "e2ee.group_encrypted", "e2ee.p2p_encrypted"].includes(payloadType)) {
25618
- const v1Msg = {
25619
- message_id: String(msg.message_id ?? ""),
25620
- from: String(msg.from_aid ?? ""),
25621
- to: String(legacy.to ?? client._aid ?? ""),
25622
- seq: msg.seq,
25623
- type: String(msg.type ?? ""),
25624
- timestamp: msg.t_server,
25625
- payload: legacyPayload,
25626
- encrypted: false
25627
- };
25628
- attachGatewayProximity(v1Msg, msg);
25629
- const appEvent = client._delivery.p2pAppEventForMessage(v1Msg);
25630
- if (ns) await client._publishPulledMessage(appEvent.event, ns, seq2, appEvent.payload, false);
25631
- else await client._publishAppEvent(appEvent.event, appEvent.payload, "pull");
25632
- decrypted.push(v1Msg);
25633
- } else if (isJsonObject(legacyPayload) && ["e2ee.p2p_encrypted", "e2ee.encrypted"].includes(payloadType)) {
25634
- const deferStatus2 = {};
25635
- const plaintext2 = await client._decryptV2Message(
25636
- { ...msg, envelope_json: JSON.stringify(legacyPayload) },
25637
- true,
25638
- true,
25639
- true,
25640
- true,
25641
- deferStatus2,
25642
- true,
25643
- "pull"
25644
- );
25645
- if (deferStatus2.deferred) {
25646
- blockedSeq = blockedSeq > 0 ? Math.min(blockedSeq, seq2) : seq2;
25647
- if (deferStatus2.fromAid) {
25648
- const key = `${deferStatus2.fromAid}\0${deferStatus2.senderDeviceId ?? ""}\0${deferStatus2.groupId ?? ""}`;
25649
- deferredKeyFetches.set(key, { fromAid: deferStatus2.fromAid, senderDeviceId: deferStatus2.senderDeviceId ?? "", groupId: deferStatus2.groupId ?? "" });
25650
- }
25651
- }
25652
- if (plaintext2) {
25653
- if (ns) await client._publishPulledMessage("message.received", ns, seq2, plaintext2, false);
25654
- else await client._publishAppEvent("message.received", plaintext2, "pull");
25655
- decrypted.push(plaintext2);
25896
+ try {
25897
+ result = await client._callRawV2Rpc("message.v2.pull", {
25898
+ after_seq: nextAfterSeq,
25899
+ limit,
25900
+ ...volatileCursor ? { after_volatile_cursor: volatileCursor } : {},
25901
+ ...opts?.force ? { force: true } : {},
25902
+ ...ackUpToSeq > 0 ? { ack_up_to_seq: ackUpToSeq } : {}
25903
+ }, pullGateKey);
25904
+ } catch (exc) {
25905
+ const text3 = exc instanceof Error ? exc.message : String(exc);
25906
+ if (!volatileCursor || !text3.toLowerCase().includes("volatile cursor epoch mismatch")) throw exc;
25907
+ client._delivery.resetVolatilePullCursor();
25908
+ volatileCursor = "";
25909
+ pageCount -= 1;
25910
+ continue;
25911
+ }
25912
+ }
25913
+ if (ackUpToSeq > 0) {
25914
+ const actualAckSeq = client._delivery?.resolveP2PPullAckSeq?.(result, ackUpToSeq) ?? 0;
25915
+ if (actualAckSeq >= ackUpToSeq) {
25916
+ if (pendingInlineGeneration !== null) {
25917
+ client._delivery?.completePendingP2PInlineAckUpTo?.(ns, actualAckSeq, pendingInlineGeneration);
25656
25918
  }
25919
+ pendingAckSeq = 0;
25920
+ lastAutoAckSeq = Math.max(lastAutoAckSeq, ackUpToSeq);
25921
+ this.clearForwardAck(ns, ackUpToSeq);
25657
25922
  } else {
25658
- client._clientLog.debug(`message.v2.pull skipping V1 envelope seq=${seq2} payload_type=${payloadType || "<none>"} (V1 E2EE removed)`);
25923
+ client._clientLog.warn(`message.v2.pull piggyback ACK below target: requested=${ackUpToSeq}, actual=${actualAckSeq}; fallback terminal ACK`);
25924
+ await this.confirmForwardP2PAck(ackUpToSeq, pendingInlineGeneration ?? void 0);
25925
+ pendingAckSeq = 0;
25926
+ lastAutoAckSeq = Math.max(lastAutoAckSeq, ackUpToSeq);
25659
25927
  }
25660
- continue;
25661
- }
25662
- if (version !== "v2") {
25663
- client._clientLog.debug(`message.v2.pull skipping non-V2 row seq=${seq2} version=${String(msg.version ?? "")}`);
25664
- continue;
25665
25928
  }
25666
- const msgSpkId = String(msg.spk_id ?? "");
25667
- if (msgSpkId && client._v2Session && !client._v2Session.isCurrentSPK(msgSpkId)) {
25668
- client._v2Session.trackOldSPKMaxSeq(msgSpkId, seq2);
25669
- }
25670
- const deferStatus = {};
25671
- const plaintext = await client._decryptV2Message(msg, true, true, true, true, deferStatus, true, "pull");
25672
- if (deferStatus.deferred) {
25673
- blockedSeq = blockedSeq > 0 ? Math.min(blockedSeq, seq2) : seq2;
25674
- }
25675
- if (deferStatus.deferred && deferStatus.fromAid) {
25676
- const key = `${deferStatus.fromAid}\0${deferStatus.senderDeviceId ?? ""}\0${deferStatus.groupId ?? ""}`;
25677
- deferredKeyFetches.set(key, {
25678
- fromAid: deferStatus.fromAid,
25679
- senderDeviceId: deferStatus.senderDeviceId ?? "",
25680
- groupId: deferStatus.groupId ?? ""
25681
- });
25929
+ const pageMessages = Array.isArray(result?.messages) ? result.messages : [];
25930
+ const volatileMessages = client._delivery.volatileMessages(pageMessages);
25931
+ if (volatileMessages.length > 0) {
25932
+ decrypted.push(...await this.publishV2VolatileMessages(volatileMessages, "pull", true));
25933
+ volatileCursor = client._delivery.volatilePullCursor();
25682
25934
  }
25683
- if (plaintext === null) continue;
25684
- if (ns) {
25685
- await client._publishPulledMessage("message.received", ns, seq2, plaintext, false);
25686
- decrypted.push(plaintext);
25687
- } else {
25688
- await client._publishAppEvent("message.received", plaintext, "pull");
25689
- decrypted.push(plaintext);
25690
- }
25691
- }
25692
- const hasServerAckSeq = Object.prototype.hasOwnProperty.call(result, "server_ack_seq");
25693
- const parsedServerAckSeq = hasServerAckSeq ? safeNonNegativeSeq(result.server_ack_seq) : null;
25694
- const serverAckSeq = parsedServerAckSeq ?? 0;
25695
- const retentionFloor = Math.max(0, Number(result.retention_floor_seq ?? 0));
25696
- if (ns) {
25697
- const commitTarget = Math.max(
25935
+ const volatileAdvanced = Boolean(volatileCursor) && volatileCursor !== volatileCursorBeforePage;
25936
+ const messages = deduplicateForwardPageMessages(pageMessages, nextAfterSeq);
25937
+ const cursor = isJsonObject(result.cursor) ? result.cursor : null;
25938
+ const hasMoreField = Object.prototype.hasOwnProperty.call(result, "has_more");
25939
+ const hasMore = result.has_more === true;
25940
+ const serverHead = safeNonNegativeSeq(cursor?.latest_seq) ?? 0;
25941
+ client._clientLog.debug(`message.v2.pull page response: page=${pageCount}, raw_count=${messages.length}, stale_count=${Math.max(0, pageMessages.length - messages.length)}, has_more=${String(result.has_more ?? "")}, server_head=${serverHead}, server_ack_seq=${String(result.server_ack_seq ?? "")}`);
25942
+ const seqs = messages.map((msg) => Number(msg.seq ?? 0)).filter((seq2) => Number.isFinite(seq2) && seq2 > 0);
25943
+ const pageContigBefore = ns ? client._seqTracker.getContiguousSeq(ns) : 0;
25944
+ const pageTrackerSnapshot = ns && typeof client._seqTracker.snapshotNamespace === "function" ? client._seqTracker.snapshotNamespace(ns) : null;
25945
+ const pageMaxSeq = seqs.length > 0 ? Math.max(...seqs) : nextAfterSeq;
25946
+ const pageContiguousSeq = contiguousPullSeq(
25698
25947
  pageContigBefore,
25699
- Number.isFinite(retentionFloor) ? retentionFloor : 0,
25700
- pageCount === 1 ? deferredServerCursor : 0,
25701
- serverAckSeq,
25702
- blockedSeq > 0 ? blockedSeq - 1 : pageContiguousSeq
25948
+ seqs,
25949
+ result.contiguous_seq
25703
25950
  );
25704
- let ackSeq = pageContigBefore;
25705
- let contigAdvanced = false;
25706
- try {
25707
- if (commitTarget > pageContigBefore) {
25708
- client._seqTracker.forceContiguousSeq(ns, commitTarget);
25709
- if (retentionFloor > pageContigBefore) {
25710
- client._clientLog.info(`message.v2.pull retention-floor advance: ns=${ns} contiguous=${pageContigBefore} -> retention_floor_seq=${retentionFloor}`);
25951
+ const deferredKeyFetches = /* @__PURE__ */ new Map();
25952
+ let blockedSeq = 0;
25953
+ const pageDecryptedBefore = decrypted.length;
25954
+ for (const msg of messages) {
25955
+ const seq2 = Number(msg.seq ?? 0);
25956
+ if (!Number.isFinite(seq2) || seq2 <= 0) continue;
25957
+ const version = String(msg.version ?? "v2");
25958
+ if (version === "v1") {
25959
+ const legacy = isJsonObject(msg.legacy_v1) ? msg.legacy_v1 : {};
25960
+ const legacyPayload = legacy.payload !== void 0 ? legacy.payload : msg.payload;
25961
+ const payloadType = isJsonObject(legacyPayload) ? String(legacyPayload.type ?? "").trim() : "";
25962
+ if (legacyPayload !== void 0 && legacyPayload !== null && !["e2ee.encrypted", "e2ee.group_encrypted", "e2ee.p2p_encrypted"].includes(payloadType)) {
25963
+ const v1Msg = {
25964
+ message_id: String(msg.message_id ?? ""),
25965
+ from: String(msg.from_aid ?? ""),
25966
+ to: String(legacy.to ?? client._aid ?? ""),
25967
+ seq: msg.seq,
25968
+ type: String(msg.type ?? ""),
25969
+ timestamp: msg.t_server,
25970
+ payload: legacyPayload,
25971
+ encrypted: false
25972
+ };
25973
+ const direction = String(msg.direction ?? "").trim();
25974
+ if (direction) v1Msg.direction = direction;
25975
+ else if (String(msg.from_aid ?? "").trim() && String(msg.from_aid ?? "").trim() !== String(client._aid ?? "").trim()) {
25976
+ v1Msg.direction = "inbound";
25977
+ }
25978
+ if (msg.sender_device_id !== void 0) v1Msg.sender_device_id = msg.sender_device_id;
25979
+ attachGatewayProximity(v1Msg, msg);
25980
+ const appEvent = client._delivery.p2pAppEventForMessage(v1Msg);
25981
+ if (ns) await client._publishPulledMessage(appEvent.event, ns, seq2, appEvent.payload, false);
25982
+ else await client._publishAppEvent(appEvent.event, appEvent.payload, "pull");
25983
+ decrypted.push(v1Msg);
25984
+ } else if (isJsonObject(legacyPayload) && ["e2ee.p2p_encrypted", "e2ee.encrypted"].includes(payloadType)) {
25985
+ const deferStatus2 = {};
25986
+ const plaintext2 = await client._decryptV2Message(
25987
+ { ...msg, envelope_json: JSON.stringify(legacyPayload) },
25988
+ true,
25989
+ true,
25990
+ true,
25991
+ true,
25992
+ deferStatus2,
25993
+ true,
25994
+ "pull"
25995
+ );
25996
+ if (deferStatus2.deferred) {
25997
+ blockedSeq = blockedSeq > 0 ? Math.min(blockedSeq, seq2) : seq2;
25998
+ if (deferStatus2.fromAid) {
25999
+ const key = `${deferStatus2.fromAid}\0${deferStatus2.senderDeviceId ?? ""}\0${deferStatus2.groupId ?? ""}`;
26000
+ deferredKeyFetches.set(key, { fromAid: deferStatus2.fromAid, senderDeviceId: deferStatus2.senderDeviceId ?? "", groupId: deferStatus2.groupId ?? "" });
26001
+ }
26002
+ }
26003
+ if (plaintext2) {
26004
+ if (ns) await client._publishPulledMessage("message.received", ns, seq2, plaintext2, false);
26005
+ else await client._publishAppEvent("message.received", plaintext2, "pull");
26006
+ decrypted.push(plaintext2);
26007
+ }
26008
+ } else {
26009
+ client._clientLog.debug(`message.v2.pull skipping V1 envelope seq=${seq2} payload_type=${payloadType || "<none>"} (V1 E2EE removed)`);
26010
+ }
26011
+ continue;
26012
+ }
26013
+ if (version !== "v2") {
26014
+ client._clientLog.debug(`message.v2.pull skipping non-V2 row seq=${seq2} version=${String(msg.version ?? "")}`);
26015
+ continue;
26016
+ }
26017
+ const msgSpkId = String(msg.spk_id ?? "");
26018
+ if (msgSpkId && client._v2Session && !client._v2Session.isCurrentSPK(msgSpkId)) {
26019
+ client._v2Session.trackOldSPKMaxSeq(msgSpkId, seq2);
26020
+ }
26021
+ const deferStatus = {};
26022
+ const plaintext = await client._decryptV2Message(msg, true, true, true, true, deferStatus, true, "pull");
26023
+ if (deferStatus.deferred) {
26024
+ blockedSeq = blockedSeq > 0 ? Math.min(blockedSeq, seq2) : seq2;
26025
+ }
26026
+ if (deferStatus.deferred && deferStatus.fromAid) {
26027
+ const key = `${deferStatus.fromAid}\0${deferStatus.senderDeviceId ?? ""}\0${deferStatus.groupId ?? ""}`;
26028
+ deferredKeyFetches.set(key, {
26029
+ fromAid: deferStatus.fromAid,
26030
+ senderDeviceId: deferStatus.senderDeviceId ?? "",
26031
+ groupId: deferStatus.groupId ?? ""
26032
+ });
26033
+ }
26034
+ if (plaintext === null) continue;
26035
+ if (ns) {
26036
+ await client._publishPulledMessage("message.received", ns, seq2, plaintext, false);
26037
+ decrypted.push(plaintext);
26038
+ } else {
26039
+ await client._publishAppEvent("message.received", plaintext, "pull");
26040
+ decrypted.push(plaintext);
26041
+ }
26042
+ }
26043
+ const hasServerAckSeq = Object.prototype.hasOwnProperty.call(result, "server_ack_seq");
26044
+ const parsedServerAckSeq = hasServerAckSeq ? safeNonNegativeSeq(result.server_ack_seq) : null;
26045
+ const serverAckSeq = parsedServerAckSeq ?? 0;
26046
+ const retentionFloor = Math.max(0, Number(result.retention_floor_seq ?? 0));
26047
+ if (ns) {
26048
+ const commitTarget = Math.max(
26049
+ pageContigBefore,
26050
+ Number.isFinite(retentionFloor) ? retentionFloor : 0,
26051
+ pageCount === 1 ? deferredServerCursor : 0,
26052
+ serverAckSeq,
26053
+ blockedSeq > 0 ? blockedSeq - 1 : pageContiguousSeq
26054
+ );
26055
+ let ackSeq = pageContigBefore;
26056
+ let contigAdvanced = false;
26057
+ try {
26058
+ if (commitTarget > pageContigBefore) {
26059
+ client._seqTracker.forceContiguousSeq(ns, commitTarget);
26060
+ if (retentionFloor > pageContigBefore) {
26061
+ client._clientLog.info(`message.v2.pull retention-floor advance: ns=${ns} contiguous=${pageContigBefore} -> retention_floor_seq=${retentionFloor}`);
26062
+ }
26063
+ if (deferredServerCursor > pageContigBefore && pageCount === 1) {
26064
+ client._clientLog.info(`message.v2.pull restore local A from deferred server ACK cursor: ns=${ns}, contiguous=${pageContigBefore} -> server_ack_seq=${deferredServerCursor}`);
26065
+ }
26066
+ if (seqs.length > 0) {
26067
+ client._clientLog.debug(`message.v2.pull force contiguous: ns=${ns}, page_max_seq=${pageMaxSeq}, previous=${pageContigBefore}`);
26068
+ }
25711
26069
  }
25712
- if (deferredServerCursor > pageContigBefore && pageCount === 1) {
25713
- client._clientLog.info(`message.v2.pull restore local A from deferred server ACK cursor: ns=${ns}, contiguous=${pageContigBefore} -> server_ack_seq=${deferredServerCursor}`);
26070
+ ackSeq = client._seqTracker.getContiguousSeq(ns);
26071
+ contigAdvanced = ackSeq !== pageContigBefore;
26072
+ if (contigAdvanced) {
26073
+ await client._commitSeqTrackerState(ns);
26074
+ if (pageCount === 1 && deferredServerCursor > 0) this.clearForwardCursor(ns, ackSeq);
25714
26075
  }
25715
- if (seqs.length > 0) {
25716
- client._clientLog.debug(`message.v2.pull force contiguous: ns=${ns}, page_max_seq=${pageMaxSeq}, previous=${pageContigBefore}`);
26076
+ } catch (exc) {
26077
+ if (typeof client._seqTracker.restoreNamespaceSnapshot === "function") {
26078
+ client._seqTracker.restoreNamespaceSnapshot(ns, pageTrackerSnapshot);
25717
26079
  }
26080
+ client._clientLog.error?.(`message.v2.pull Forward commit rolled back: ns=${ns} target=${commitTarget} error=${formatE2EEError(exc)}`);
26081
+ throw exc;
25718
26082
  }
25719
- ackSeq = client._seqTracker.getContiguousSeq(ns);
25720
- contigAdvanced = ackSeq !== pageContigBefore;
25721
- if (contigAdvanced) {
25722
- await client._commitSeqTrackerState(ns);
25723
- if (pageCount === 1 && deferredServerCursor > 0) this.clearForwardCursor(ns, ackSeq);
26083
+ if (contigAdvanced) await client._drainOrderedMessages(ns, void 0, false, false);
26084
+ for (const fetch2 of deferredKeyFetches.values()) {
26085
+ this.scheduleSenderIKFetch(fetch2.fromAid, fetch2.senderDeviceId, fetch2.groupId);
25724
26086
  }
25725
- } catch (exc) {
25726
- if (typeof client._seqTracker.restoreNamespaceSnapshot === "function") {
25727
- client._seqTracker.restoreNamespaceSnapshot(ns, pageTrackerSnapshot);
26087
+ if (parsedServerAckSeq !== null && serverAckSeq > ackSeq) {
26088
+ this.recordForwardCursor(ns, serverAckSeq, ackSeq);
25728
26089
  }
25729
- client._clientLog.error?.(`message.v2.pull Forward commit rolled back: ns=${ns} target=${commitTarget} error=${formatE2EEError(exc)}`);
25730
- throw exc;
25731
- }
25732
- if (contigAdvanced) await client._drainOrderedMessages(ns, void 0, false, false);
25733
- for (const fetch2 of deferredKeyFetches.values()) {
25734
- this.scheduleSenderIKFetch(fetch2.fromAid, fetch2.senderDeviceId, fetch2.groupId);
25735
- }
25736
- if (parsedServerAckSeq !== null && serverAckSeq > ackSeq) {
25737
- this.recordForwardCursor(ns, serverAckSeq, ackSeq);
25738
- }
25739
- const knownServerAckSeq = hasServerAckSeq ? serverAckSeq : pageCount === 1 ? deferredServerCursor : 0;
25740
- const ackNeeded = ackSeq > 0 && ackSeq > lastAutoAckSeq && (hasServerAckSeq && ackSeq > serverAckSeq || contigAdvanced && ackSeq > knownServerAckSeq);
25741
- if (ackNeeded) {
25742
- this.recordForwardAck(ns, ackSeq);
25743
- const nextAfter2 = Math.max(pageContiguousSeq, nextAfterSeq);
25744
- const canContinuePage = blockedSeq <= 0 && messages.length > 0 && nextAfter2 > nextAfterSeq;
25745
- if (canContinuePage) {
25746
- pendingAckSeq = Math.max(pendingAckSeq, ackSeq);
25747
- lastAutoAckSeq = Math.max(lastAutoAckSeq, ackSeq);
25748
- } else {
25749
- await this.confirmForwardP2PAck(ackSeq);
25750
- lastAutoAckSeq = Math.max(lastAutoAckSeq, ackSeq);
26090
+ const knownServerAckSeq = hasServerAckSeq ? serverAckSeq : pageCount === 1 ? deferredServerCursor : 0;
26091
+ const ackNeeded = ackSeq > 0 && ackSeq > lastAutoAckSeq && (hasServerAckSeq && ackSeq > serverAckSeq || contigAdvanced && ackSeq > knownServerAckSeq);
26092
+ if (ackNeeded) {
26093
+ this.recordForwardAck(ns, ackSeq);
26094
+ const nextAfter2 = Math.max(pageContiguousSeq, nextAfterSeq);
26095
+ const canContinuePage = blockedSeq <= 0 && messages.length > 0 && nextAfter2 > nextAfterSeq;
26096
+ if (canContinuePage && !pipelineRunning) {
26097
+ pendingAckSeq = Math.max(pendingAckSeq, ackSeq);
26098
+ lastAutoAckSeq = Math.max(lastAutoAckSeq, ackSeq);
26099
+ } else {
26100
+ await this.confirmForwardP2PAck(ackSeq);
26101
+ lastAutoAckSeq = Math.max(lastAutoAckSeq, ackSeq);
26102
+ }
25751
26103
  }
25752
26104
  }
26105
+ if (ns) client._delivery.onPullPage?.(
26106
+ ns,
26107
+ decrypted.length - pageDecryptedBefore,
26108
+ hasMore,
26109
+ pageMaxSeq,
26110
+ typeof result.remaining === "number" && Number.isSafeInteger(result.remaining) && result.remaining >= 0 ? result.remaining : null,
26111
+ messages.length,
26112
+ client._seqTracker.getContiguousSeq(ns),
26113
+ pageMaxSeq
26114
+ );
26115
+ const nextAfter = Math.max(pageContiguousSeq, nextAfterSeq);
26116
+ const rawCount = messages.length;
26117
+ if (blockedSeq > 0) break;
26118
+ let shouldContinue = shouldContinueForwardPage({
26119
+ rawCount,
26120
+ nextAfterSeq,
26121
+ nextAfter,
26122
+ limit,
26123
+ hasMoreField,
26124
+ hasMore,
26125
+ serverHead,
26126
+ pendingAckSeq,
26127
+ observedUpper: typeof client._seqTracker.getMaxSeenSeq === "function" ? client._seqTracker.getMaxSeenSeq(ns) : nextAfter
26128
+ });
26129
+ const volatileHasMoreField = Object.prototype.hasOwnProperty.call(result, "volatile_has_more");
26130
+ const volatileHasMore = result.volatile_has_more === true;
26131
+ if (!shouldContinue && volatileAdvanced && (volatileHasMore || hasMore || !volatileHasMoreField && limit > 0 && pageMessages.length >= limit)) {
26132
+ shouldContinue = true;
26133
+ }
26134
+ const shouldContinueForPipeline = shouldContinueForwardPage({
26135
+ rawCount,
26136
+ nextAfterSeq,
26137
+ nextAfter,
26138
+ limit,
26139
+ hasMoreField,
26140
+ hasMore,
26141
+ serverHead,
26142
+ pendingAckSeq: 0,
26143
+ observedUpper: typeof client._seqTracker.getMaxSeenSeq === "function" ? client._seqTracker.getMaxSeenSeq(ns) : nextAfter
26144
+ });
26145
+ if (!shouldContinue) break;
26146
+ if (shouldContinueForPipeline && !volatileAdvanced && !pipelineRunning && limit >= V2_PULL_MAX_PAGE_LIMIT) {
26147
+ if (pendingAckSeq > 0) {
26148
+ await this.confirmForwardP2PAck(pendingAckSeq);
26149
+ pendingAckSeq = 0;
26150
+ }
26151
+ startPipeline(nextAfter, volatileCursor);
26152
+ }
26153
+ if (!pipelineRunning && pullGateKey && opts?.gateLocked && client._rpcPipeline?.yieldPullGate) {
26154
+ const nextKey = pullGateKeyForClient(client, "message.v2.pull", {
26155
+ after_seq: nextAfter,
26156
+ after_volatile_cursor: volatileCursor,
26157
+ force: opts?.force === true,
26158
+ limit
26159
+ }, `${ns}|forward|after=${nextAfter}|force=${String(Boolean(opts?.force))}|limit=${limit}`);
26160
+ await client._rpcPipeline.yieldPullGate(pullGateKey, nextKey, true);
26161
+ pullGateKey = nextKey;
26162
+ }
26163
+ const tailDelayMs = v2PullTailDelayMs(rawCount, limit);
26164
+ if (tailDelayMs > 0) await v2Sleep(tailDelayMs);
26165
+ nextAfterSeq = nextAfter;
25753
26166
  }
25754
- if (ns) client._delivery.onPullPage?.(
25755
- ns,
25756
- decrypted.length - pageDecryptedBefore,
25757
- hasMore,
25758
- pageMaxSeq,
25759
- typeof result.remaining === "number" && Number.isSafeInteger(result.remaining) && result.remaining >= 0 ? result.remaining : null,
25760
- messages.length,
25761
- client._seqTracker.getContiguousSeq(ns),
25762
- pageMaxSeq
25763
- );
25764
- const nextAfter = Math.max(pageContiguousSeq, nextAfterSeq);
25765
- const rawCount = messages.length;
25766
- if (blockedSeq > 0) break;
25767
- let shouldContinue = shouldContinueForwardPage({
25768
- rawCount,
25769
- nextAfterSeq,
25770
- nextAfter,
25771
- limit,
25772
- hasMoreField,
25773
- hasMore,
25774
- serverHead,
25775
- pendingAckSeq,
25776
- observedUpper: typeof client._seqTracker.getMaxSeenSeq === "function" ? client._seqTracker.getMaxSeenSeq(ns) : nextAfter
25777
- });
25778
- const volatileHasMoreField = Object.prototype.hasOwnProperty.call(result, "volatile_has_more");
25779
- const volatileHasMore = result.volatile_has_more === true;
25780
- if (!shouldContinue && volatileAdvanced && (volatileHasMore || hasMore || !volatileHasMoreField && limit > 0 && pageMessages.length >= limit)) {
25781
- shouldContinue = true;
25782
- }
25783
- if (!shouldContinue) break;
25784
- if (pullGateKey && opts?.gateLocked && client._rpcPipeline?.yieldPullGate) {
25785
- const nextKey = pullGateKeyForClient(client, "message.v2.pull", {
25786
- after_seq: nextAfter,
25787
- after_volatile_cursor: volatileCursor,
25788
- force: opts?.force === true,
25789
- limit
25790
- }, `${ns}|forward|after=${nextAfter}|force=${String(Boolean(opts?.force))}|limit=${limit}`);
25791
- await client._rpcPipeline.yieldPullGate(pullGateKey, nextKey, true);
25792
- pullGateKey = nextKey;
25793
- }
25794
- const tailDelayMs = v2PullTailDelayMs(rawCount, limit);
25795
- if (tailDelayMs > 0) await v2Sleep(tailDelayMs);
25796
- nextAfterSeq = nextAfter;
26167
+ } finally {
26168
+ await stopPipeline();
25797
26169
  }
25798
26170
  if (pageCount >= maxPages) {
25799
26171
  client._clientLog.warn(`message.v2.pull reached max_pages=${maxPages} after_seq=${nextAfterSeq}`);
@@ -26156,79 +26528,182 @@ var V2E2EECoordinator = class {
26156
26528
  let lastAutoAckSeq = 0;
26157
26529
  let pendingAckSeq = deferredForwardAck;
26158
26530
  const maxPages = Math.max(1, Math.min(100, Math.trunc(Number(opts?.maxPages ?? 100) || 100)));
26159
- while (pageCount < maxPages) {
26160
- pageCount += 1;
26161
- const ackUpToSeq = ownsCursor ? pendingAckSeq : 0;
26162
- const pendingInlineGeneration = ackUpToSeq > 0 ? client._delivery?.pendingGroupInlineAckGeneration?.(ns) ?? null : null;
26163
- const result = await client._callRawV2Rpc("group.v2.pull", withExplicitGroupAid({
26164
- group_id: wireGroupId,
26165
- after_seq: nextAfterSeq,
26166
- limit,
26167
- ...cursorParams,
26168
- ...opts?.force ? { force: true } : {},
26169
- ...ackUpToSeq > 0 ? { ack_up_to_seq: ackUpToSeq } : {}
26170
- }, groupAid), pullGateKey);
26171
- if (ackUpToSeq > 0) {
26172
- const actualAckSeq = client._delivery?.resolveGroupPullAckSeq?.(result, ackUpToSeq) ?? 0;
26173
- if (actualAckSeq >= ackUpToSeq) {
26174
- if (pendingInlineGeneration !== null) {
26175
- client._delivery?.completePendingGroupInlineAckUpTo?.(ns, actualAckSeq, pendingInlineGeneration);
26531
+ let pipelineRunning = false;
26532
+ let pipelineError = null;
26533
+ const pipelineQueue = new V2PullPageQueue(V2_PULL_PIPELINE_QUEUE_CAPACITY);
26534
+ let pipelineProducer = null;
26535
+ let pipelineStopped = false;
26536
+ const startPipeline = (startAfterSeq) => {
26537
+ if (pipelineRunning || pageCount >= maxPages || limit < V2_PULL_MAX_PAGE_LIMIT || !ownsCursor) return;
26538
+ pipelineRunning = true;
26539
+ pipelineProducer = (async () => {
26540
+ let producerAfterSeq = startAfterSeq;
26541
+ for (let produced = pageCount; produced < maxPages; produced += 1) {
26542
+ if (pipelineStopped) break;
26543
+ const result = await client._callRawV2Rpc("group.v2.pull", withExplicitGroupAid({
26544
+ group_id: wireGroupId,
26545
+ after_seq: producerAfterSeq,
26546
+ limit,
26547
+ ...cursorParams,
26548
+ ...opts?.force ? { force: true } : {}
26549
+ }, groupAid), pullGateKey);
26550
+ if (pipelineStopped) break;
26551
+ const pageMessages = Array.isArray(result?.messages) ? result.messages : [];
26552
+ const enqueued = await pipelineQueue.put({
26553
+ result,
26554
+ afterSeq: producerAfterSeq,
26555
+ ackUpToSeq: 0,
26556
+ inlineAckGeneration: null,
26557
+ requestedVolatileCursor: "",
26558
+ weight: pageMessages.length
26559
+ }, pageMessages.length);
26560
+ if (!enqueued || pipelineStopped) break;
26561
+ const messages = deduplicateForwardPageMessages(pageMessages, producerAfterSeq);
26562
+ const seqs = messages.map((msg) => Number(msg.seq ?? 0)).filter((seq2) => Number.isFinite(seq2) && seq2 > 0);
26563
+ const nextAfter = Math.max(contiguousPullSeq(producerAfterSeq, seqs, result.contiguous_seq), producerAfterSeq);
26564
+ const cursor = isJsonObject(result.cursor) ? result.cursor : null;
26565
+ const shouldContinue = shouldContinueForwardPage({
26566
+ rawCount: messages.length,
26567
+ nextAfterSeq: producerAfterSeq,
26568
+ nextAfter,
26569
+ limit,
26570
+ hasMoreField: Object.prototype.hasOwnProperty.call(result, "has_more"),
26571
+ hasMore: result.has_more === true,
26572
+ serverHead: Math.max(safeNonNegativeSeq(result.latest_message_seq) ?? 0, safeNonNegativeSeq(cursor?.latest_seq) ?? 0),
26573
+ observedUpper: Math.max(nextAfter, typeof client._seqTracker.getMaxSeenSeq === "function" ? client._seqTracker.getMaxSeenSeq(ns) : 0),
26574
+ pendingAckSeq: 0
26575
+ });
26576
+ if (!shouldContinue) break;
26577
+ producerAfterSeq = nextAfter;
26578
+ }
26579
+ })().catch((error) => {
26580
+ pipelineError = error;
26581
+ }).finally(() => {
26582
+ pipelineQueue.close();
26583
+ });
26584
+ };
26585
+ const stopPipeline = async () => {
26586
+ pipelineStopped = true;
26587
+ pipelineQueue.close();
26588
+ if (pipelineProducer) {
26589
+ const producer = pipelineProducer;
26590
+ pipelineProducer = null;
26591
+ await producer;
26592
+ }
26593
+ pipelineRunning = false;
26594
+ };
26595
+ try {
26596
+ while (pageCount < maxPages) {
26597
+ let pipelinePage;
26598
+ if (pipelineRunning) {
26599
+ pipelinePage = await pipelineQueue.take();
26600
+ if (!pipelinePage) {
26601
+ pipelineRunning = false;
26602
+ if (pipelineError) throw pipelineError;
26603
+ break;
26176
26604
  }
26177
- pendingAckSeq = 0;
26178
- lastAutoAckSeq = Math.max(lastAutoAckSeq, ackUpToSeq);
26179
- this.clearForwardAck(ns, ackUpToSeq);
26605
+ }
26606
+ pageCount += 1;
26607
+ const ackUpToSeq = ownsCursor ? pipelinePage?.ackUpToSeq ?? pendingAckSeq : 0;
26608
+ const pendingInlineGeneration = pipelinePage?.inlineAckGeneration ?? (ackUpToSeq > 0 ? client._delivery?.pendingGroupInlineAckGeneration?.(ns) ?? null : null);
26609
+ let result;
26610
+ if (pipelinePage) {
26611
+ nextAfterSeq = pipelinePage.afterSeq;
26612
+ result = pipelinePage.result;
26180
26613
  } else {
26181
- client._clientLog.warn(`group.v2.pull piggyback ACK below target: group=${gid}, requested=${ackUpToSeq}, actual=${actualAckSeq}; fallback terminal ACK`);
26182
- await this.confirmForwardGroupAck(gid, ackUpToSeq, groupAid);
26183
- pendingAckSeq = 0;
26184
- lastAutoAckSeq = Math.max(lastAutoAckSeq, ackUpToSeq);
26185
- }
26186
- }
26187
- const resultGroupAid = String(result.group_aid ?? result.groupAid ?? "").trim();
26188
- if (resultGroupAid) groupAid = resultGroupAid;
26189
- const eventGroupAid = resultGroupAid || groupAid || gid;
26190
- const pageMessages = Array.isArray(result?.messages) ? result.messages : [];
26191
- const messages = deduplicateForwardPageMessages(pageMessages, nextAfterSeq);
26192
- const cursor = isJsonObject(result.cursor) ? result.cursor : null;
26193
- const hasMoreField = Object.prototype.hasOwnProperty.call(result, "has_more");
26194
- const hasMore = result.has_more === true;
26195
- const serverHead = Math.max(
26196
- safeNonNegativeSeq(result.latest_message_seq) ?? 0,
26197
- safeNonNegativeSeq(cursor?.latest_seq) ?? 0
26198
- );
26199
- const seqs = messages.map((msg) => Number(msg.seq ?? 0)).filter((seq2) => Number.isFinite(seq2) && seq2 > 0);
26200
- const pageContigBefore = client._seqTracker.getContiguousSeq(ns);
26201
- const pageTrackerSnapshot = typeof client._seqTracker.snapshotNamespace === "function" ? client._seqTracker.snapshotNamespace(ns) : null;
26202
- const pageMaxSeq = seqs.length > 0 ? Math.max(...seqs) : nextAfterSeq;
26203
- const pageContiguousSeq = contiguousPullSeq(
26204
- pageContigBefore,
26205
- seqs,
26206
- result.contiguous_seq
26207
- );
26208
- const deferredKeyFetches = /* @__PURE__ */ new Map();
26209
- let blockedSeq = 0;
26210
- const pageDecryptedBefore = decrypted.length;
26211
- for (const msg of messages) {
26212
- const seq2 = Number(msg.seq ?? 0);
26213
- if (!Number.isFinite(seq2) || seq2 <= 0) continue;
26214
- const version = String(msg.version ?? "v2");
26215
- if (version === "v1") {
26216
- const legacy = isJsonObject(msg.legacy_v1) ? msg.legacy_v1 : {};
26217
- const payload = msg.payload !== void 0 ? msg.payload : legacy.payload;
26218
- const payloadObj = isJsonObject(payload) ? payload : null;
26219
- if (typeof client._delivery?.recallEventFromGroupMessage === "function" && client._delivery.recallEventFromGroupMessage(msg)) {
26220
- await client._delivery.publishGroupRecallTombstone(gid, seq2, {
26221
- ...msg,
26222
- group_id: gid,
26223
- group_aid: eventGroupAid
26224
- }, "pull");
26225
- client._markPublishedSeq(ns, seq2);
26226
- client._clientLog.debug(`group.v2.pull recall tombstone delivered: group=${gid}, seq=${seq2}`);
26227
- continue;
26614
+ result = await client._callRawV2Rpc("group.v2.pull", withExplicitGroupAid({
26615
+ group_id: wireGroupId,
26616
+ after_seq: nextAfterSeq,
26617
+ limit,
26618
+ ...cursorParams,
26619
+ ...opts?.force ? { force: true } : {},
26620
+ ...ackUpToSeq > 0 ? { ack_up_to_seq: ackUpToSeq } : {}
26621
+ }, groupAid), pullGateKey);
26622
+ }
26623
+ if (ackUpToSeq > 0) {
26624
+ const actualAckSeq = client._delivery?.resolveGroupPullAckSeq?.(result, ackUpToSeq) ?? 0;
26625
+ if (actualAckSeq >= ackUpToSeq) {
26626
+ if (pendingInlineGeneration !== null) {
26627
+ client._delivery?.completePendingGroupInlineAckUpTo?.(ns, actualAckSeq, pendingInlineGeneration);
26628
+ }
26629
+ pendingAckSeq = 0;
26630
+ lastAutoAckSeq = Math.max(lastAutoAckSeq, ackUpToSeq);
26631
+ this.clearForwardAck(ns, ackUpToSeq);
26632
+ } else {
26633
+ client._clientLog.warn(`group.v2.pull piggyback ACK below target: group=${gid}, requested=${ackUpToSeq}, actual=${actualAckSeq}; fallback terminal ACK`);
26634
+ await this.confirmForwardGroupAck(gid, ackUpToSeq, groupAid);
26635
+ pendingAckSeq = 0;
26636
+ lastAutoAckSeq = Math.max(lastAutoAckSeq, ackUpToSeq);
26228
26637
  }
26229
- if (payloadObj) {
26230
- const payloadType = String(payloadObj.type ?? "").trim();
26231
- if (payloadType !== "e2ee.encrypted" && payloadType !== "e2ee.group_encrypted" && payloadType !== "e2ee.p2p_encrypted") {
26638
+ }
26639
+ const resultGroupAid = String(result.group_aid ?? result.groupAid ?? "").trim();
26640
+ if (resultGroupAid) groupAid = resultGroupAid;
26641
+ const eventGroupAid = resultGroupAid || groupAid || gid;
26642
+ const pageMessages = Array.isArray(result?.messages) ? result.messages : [];
26643
+ const messages = deduplicateForwardPageMessages(pageMessages, nextAfterSeq);
26644
+ const cursor = isJsonObject(result.cursor) ? result.cursor : null;
26645
+ const hasMoreField = Object.prototype.hasOwnProperty.call(result, "has_more");
26646
+ const hasMore = result.has_more === true;
26647
+ const serverHead = Math.max(
26648
+ safeNonNegativeSeq(result.latest_message_seq) ?? 0,
26649
+ safeNonNegativeSeq(cursor?.latest_seq) ?? 0
26650
+ );
26651
+ const seqs = messages.map((msg) => Number(msg.seq ?? 0)).filter((seq2) => Number.isFinite(seq2) && seq2 > 0);
26652
+ const pageContigBefore = client._seqTracker.getContiguousSeq(ns);
26653
+ const pageTrackerSnapshot = typeof client._seqTracker.snapshotNamespace === "function" ? client._seqTracker.snapshotNamespace(ns) : null;
26654
+ const pageMaxSeq = seqs.length > 0 ? Math.max(...seqs) : nextAfterSeq;
26655
+ const pageContiguousSeq = contiguousPullSeq(
26656
+ pageContigBefore,
26657
+ seqs,
26658
+ result.contiguous_seq
26659
+ );
26660
+ const deferredKeyFetches = /* @__PURE__ */ new Map();
26661
+ let blockedSeq = 0;
26662
+ const pageDecryptedBefore = decrypted.length;
26663
+ for (const msg of messages) {
26664
+ const seq2 = Number(msg.seq ?? 0);
26665
+ if (!Number.isFinite(seq2) || seq2 <= 0) continue;
26666
+ const version = String(msg.version ?? "v2");
26667
+ if (version === "v1") {
26668
+ const legacy = isJsonObject(msg.legacy_v1) ? msg.legacy_v1 : {};
26669
+ const payload = msg.payload !== void 0 ? msg.payload : legacy.payload;
26670
+ const payloadObj = isJsonObject(payload) ? payload : null;
26671
+ if (typeof client._delivery?.recallEventFromGroupMessage === "function" && client._delivery.recallEventFromGroupMessage(msg)) {
26672
+ await client._delivery.publishGroupRecallTombstone(gid, seq2, {
26673
+ ...msg,
26674
+ group_id: gid,
26675
+ group_aid: eventGroupAid
26676
+ }, "pull");
26677
+ client._markPublishedSeq(ns, seq2);
26678
+ client._clientLog.debug(`group.v2.pull recall tombstone delivered: group=${gid}, seq=${seq2}`);
26679
+ continue;
26680
+ }
26681
+ if (payloadObj) {
26682
+ const payloadType = String(payloadObj.type ?? "").trim();
26683
+ if (payloadType !== "e2ee.encrypted" && payloadType !== "e2ee.group_encrypted" && payloadType !== "e2ee.p2p_encrypted") {
26684
+ let v1Msg = {
26685
+ message_id: String(msg.message_id ?? ""),
26686
+ from: String(msg.from_aid ?? ""),
26687
+ group_id: gid,
26688
+ group_aid: eventGroupAid,
26689
+ seq: msg.seq,
26690
+ type: String(msg.type ?? ""),
26691
+ timestamp: msg.t_server,
26692
+ payload,
26693
+ encrypted: false
26694
+ };
26695
+ if (msg.sender_device_id !== void 0) v1Msg.sender_device_id = msg.sender_device_id;
26696
+ v1Msg = normalizeGroupMentionMode(v1Msg, msg, msg.envelope_json);
26697
+ attachGatewayProximity(v1Msg, msg);
26698
+ if (!client._delivery.isSelfSentGroupMessage?.(msg)) {
26699
+ await client._publishPulledMessage("group.message_created", ns, seq2, v1Msg, false);
26700
+ } else {
26701
+ client._markPublishedSeq(ns, seq2);
26702
+ }
26703
+ decrypted.push(v1Msg);
26704
+ continue;
26705
+ }
26706
+ } else if (payload !== void 0 && payload !== null) {
26232
26707
  let v1Msg = {
26233
26708
  message_id: String(msg.message_id ?? ""),
26234
26709
  from: String(msg.from_aid ?? ""),
@@ -26240,206 +26715,206 @@ var V2E2EECoordinator = class {
26240
26715
  payload,
26241
26716
  encrypted: false
26242
26717
  };
26718
+ if (msg.sender_device_id !== void 0) v1Msg.sender_device_id = msg.sender_device_id;
26243
26719
  v1Msg = normalizeGroupMentionMode(v1Msg, msg, msg.envelope_json);
26244
26720
  attachGatewayProximity(v1Msg, msg);
26245
- if (!client._delivery.isSelfSentGroupMessage?.(msg)) {
26246
- await client._publishPulledMessage("group.message_created", ns, seq2, v1Msg, false);
26247
- } else {
26248
- client._markPublishedSeq(ns, seq2);
26249
- }
26721
+ await client._publishPulledMessage("group.message_created", ns, seq2, v1Msg, false);
26250
26722
  decrypted.push(v1Msg);
26251
26723
  continue;
26252
26724
  }
26253
- } else if (payload !== void 0 && payload !== null) {
26254
- let v1Msg = {
26255
- message_id: String(msg.message_id ?? ""),
26256
- from: String(msg.from_aid ?? ""),
26257
- group_id: gid,
26258
- group_aid: eventGroupAid,
26259
- seq: msg.seq,
26260
- type: String(msg.type ?? ""),
26261
- timestamp: msg.t_server,
26262
- payload,
26263
- encrypted: false
26264
- };
26265
- v1Msg = normalizeGroupMentionMode(v1Msg, msg, msg.envelope_json);
26266
- attachGatewayProximity(v1Msg, msg);
26267
- await client._publishPulledMessage("group.message_created", ns, seq2, v1Msg, false);
26268
- decrypted.push(v1Msg);
26269
- continue;
26270
- }
26271
- if (payloadObj && ["e2ee.group_encrypted", "e2ee.p2p_encrypted", "e2ee.encrypted"].includes(String(payloadObj.type ?? "").trim())) {
26272
- const deferStatus2 = {};
26273
- const plaintext2 = await client._decryptV2Message(
26274
- { ...msg, envelope_json: JSON.stringify(payloadObj) },
26275
- true,
26276
- true,
26277
- true,
26278
- true,
26279
- deferStatus2,
26280
- true,
26281
- "pull"
26282
- );
26283
- if (deferStatus2.deferred) {
26284
- blockedSeq = blockedSeq > 0 ? Math.min(blockedSeq, seq2) : seq2;
26285
- if (deferStatus2.fromAid) {
26286
- const key = `${deferStatus2.fromAid}\0${deferStatus2.senderDeviceId ?? ""}\0${deferStatus2.groupId ?? ""}`;
26287
- deferredKeyFetches.set(key, { fromAid: deferStatus2.fromAid, senderDeviceId: deferStatus2.senderDeviceId ?? "", groupId: deferStatus2.groupId ?? "" });
26725
+ if (payloadObj && ["e2ee.group_encrypted", "e2ee.p2p_encrypted", "e2ee.encrypted"].includes(String(payloadObj.type ?? "").trim())) {
26726
+ const deferStatus2 = {};
26727
+ const plaintext2 = await client._decryptV2Message(
26728
+ { ...msg, envelope_json: JSON.stringify(payloadObj) },
26729
+ true,
26730
+ true,
26731
+ true,
26732
+ true,
26733
+ deferStatus2,
26734
+ true,
26735
+ "pull"
26736
+ );
26737
+ if (deferStatus2.deferred) {
26738
+ blockedSeq = blockedSeq > 0 ? Math.min(blockedSeq, seq2) : seq2;
26739
+ if (deferStatus2.fromAid) {
26740
+ const key = `${deferStatus2.fromAid}\0${deferStatus2.senderDeviceId ?? ""}\0${deferStatus2.groupId ?? ""}`;
26741
+ deferredKeyFetches.set(key, { fromAid: deferStatus2.fromAid, senderDeviceId: deferStatus2.senderDeviceId ?? "", groupId: deferStatus2.groupId ?? "" });
26742
+ }
26288
26743
  }
26289
- }
26290
- if (plaintext2) {
26291
- plaintext2.group_id = gid;
26292
- plaintext2.group_aid = eventGroupAid;
26293
- if (!client._delivery.isSelfSentGroupMessage?.(msg)) {
26294
- await client._publishPulledMessage("group.message_created", ns, seq2, plaintext2, false);
26295
- } else {
26296
- client._markPublishedSeq(ns, seq2);
26744
+ if (plaintext2) {
26745
+ plaintext2.group_id = gid;
26746
+ plaintext2.group_aid = eventGroupAid;
26747
+ if (!client._delivery.isSelfSentGroupMessage?.(msg)) {
26748
+ await client._publishPulledMessage("group.message_created", ns, seq2, plaintext2, false);
26749
+ } else {
26750
+ client._markPublishedSeq(ns, seq2);
26751
+ }
26752
+ decrypted.push(plaintext2);
26297
26753
  }
26298
- decrypted.push(plaintext2);
26299
26754
  }
26755
+ client._clientLog.debug(`group.v2.pull skipping V1 envelope group=${gid} seq=${seq2} payload_type=${payloadObj ? String(payloadObj.type ?? "") : "<none>"} (V1 E2EE removed)`);
26756
+ continue;
26300
26757
  }
26301
- client._clientLog.debug(`group.v2.pull skipping V1 envelope group=${gid} seq=${seq2} payload_type=${payloadObj ? String(payloadObj.type ?? "") : "<none>"} (V1 E2EE removed)`);
26302
- continue;
26303
- }
26304
- if (version !== "v2") {
26305
- client._clientLog.debug(`group.v2.pull skipping non-V2 row group=${gid} seq=${seq2} version=${String(msg.version ?? "")}`);
26306
- continue;
26307
- }
26308
- const deferStatus = {};
26309
- let plaintext = await client._decryptV2Message(msg, true, true, true, true, deferStatus, true, "pull");
26310
- if (deferStatus.deferred) {
26311
- blockedSeq = blockedSeq > 0 ? Math.min(blockedSeq, seq2) : seq2;
26312
- }
26313
- if (deferStatus.deferred && deferStatus.fromAid) {
26314
- const key = `${deferStatus.fromAid}\0${deferStatus.senderDeviceId ?? ""}\0${deferStatus.groupId ?? ""}`;
26315
- deferredKeyFetches.set(key, {
26316
- fromAid: deferStatus.fromAid,
26317
- senderDeviceId: deferStatus.senderDeviceId ?? "",
26318
- groupId: deferStatus.groupId ?? ""
26319
- });
26320
- }
26321
- if (plaintext === null) continue;
26322
- plaintext = normalizeGroupMentionMode(plaintext, msg, msg.envelope_json);
26323
- plaintext.group_id = gid;
26324
- plaintext.group_aid = eventGroupAid;
26325
- if (!client._delivery.isSelfSentGroupMessage?.(msg)) {
26326
- await client._publishPulledMessage("group.message_created", ns, seq2, plaintext, false);
26327
- } else {
26328
- client._markPublishedSeq(ns, seq2);
26758
+ if (version !== "v2") {
26759
+ client._clientLog.debug(`group.v2.pull skipping non-V2 row group=${gid} seq=${seq2} version=${String(msg.version ?? "")}`);
26760
+ continue;
26761
+ }
26762
+ const deferStatus = {};
26763
+ let plaintext = await client._decryptV2Message(msg, true, true, true, true, deferStatus, true, "pull");
26764
+ if (deferStatus.deferred) {
26765
+ blockedSeq = blockedSeq > 0 ? Math.min(blockedSeq, seq2) : seq2;
26766
+ }
26767
+ if (deferStatus.deferred && deferStatus.fromAid) {
26768
+ const key = `${deferStatus.fromAid}\0${deferStatus.senderDeviceId ?? ""}\0${deferStatus.groupId ?? ""}`;
26769
+ deferredKeyFetches.set(key, {
26770
+ fromAid: deferStatus.fromAid,
26771
+ senderDeviceId: deferStatus.senderDeviceId ?? "",
26772
+ groupId: deferStatus.groupId ?? ""
26773
+ });
26774
+ }
26775
+ if (plaintext === null) continue;
26776
+ plaintext = normalizeGroupMentionMode(plaintext, msg, msg.envelope_json);
26777
+ plaintext.group_id = gid;
26778
+ plaintext.group_aid = eventGroupAid;
26779
+ if (!client._delivery.isSelfSentGroupMessage?.(msg)) {
26780
+ await client._publishPulledMessage("group.message_created", ns, seq2, plaintext, false);
26781
+ } else {
26782
+ client._markPublishedSeq(ns, seq2);
26783
+ }
26784
+ decrypted.push(plaintext);
26329
26785
  }
26330
- decrypted.push(plaintext);
26331
- }
26332
- const hasServerCursor = cursor !== null && Object.prototype.hasOwnProperty.call(cursor, "current_seq");
26333
- const parsedCursorCurrentSeq = hasServerCursor ? safeNonNegativeSeq(cursor?.current_seq) : null;
26334
- const cursorCurrentSeq = parsedCursorCurrentSeq ?? 0;
26335
- const retentionFloor = Math.max(
26336
- Number(result.retention_floor_message_seq ?? result.retention_floor_seq ?? 0),
26337
- Number(cursor?.retention_floor_seq ?? 0)
26338
- );
26339
- const visibilityFloor = Math.max(
26340
- 0,
26341
- Math.max(0, Number(result.earliest_available_message_seq ?? 1) - 1),
26342
- Number(cursor?.join_seq ?? 0)
26343
- );
26344
- const effectiveFloor = Math.max(retentionFloor, visibilityFloor);
26345
- const commitTarget = Math.max(
26346
- pageContigBefore,
26347
- Number.isFinite(effectiveFloor) ? effectiveFloor : 0,
26348
- ownsCursor && pageCount === 1 ? deferredServerCursor : 0,
26349
- cursorCurrentSeq,
26350
- blockedSeq > 0 ? blockedSeq - 1 : pageContiguousSeq
26351
- );
26352
- let ackSeq = pageContigBefore;
26353
- let contigAdvanced = false;
26354
- if (ownsCursor) {
26355
- try {
26356
- if (commitTarget > pageContigBefore) {
26357
- client._seqTracker.forceContiguousSeq(ns, commitTarget);
26358
- if (effectiveFloor > pageContigBefore) {
26359
- client._clientLog.info(`group.v2.pull floor advance: ns=${ns} contiguous=${pageContigBefore} -> effective_floor=${effectiveFloor} retention_floor=${retentionFloor} visibility_floor=${visibilityFloor}`);
26786
+ const hasServerCursor = cursor !== null && Object.prototype.hasOwnProperty.call(cursor, "current_seq");
26787
+ const parsedCursorCurrentSeq = hasServerCursor ? safeNonNegativeSeq(cursor?.current_seq) : null;
26788
+ const cursorCurrentSeq = parsedCursorCurrentSeq ?? 0;
26789
+ const retentionFloor = Math.max(
26790
+ Number(result.retention_floor_message_seq ?? result.retention_floor_seq ?? 0),
26791
+ Number(cursor?.retention_floor_seq ?? 0)
26792
+ );
26793
+ const visibilityFloor = Math.max(
26794
+ 0,
26795
+ Math.max(0, Number(result.earliest_available_message_seq ?? 1) - 1),
26796
+ Number(cursor?.join_seq ?? 0)
26797
+ );
26798
+ const effectiveFloor = Math.max(retentionFloor, visibilityFloor);
26799
+ const commitTarget = Math.max(
26800
+ pageContigBefore,
26801
+ Number.isFinite(effectiveFloor) ? effectiveFloor : 0,
26802
+ ownsCursor && pageCount === 1 ? deferredServerCursor : 0,
26803
+ cursorCurrentSeq,
26804
+ blockedSeq > 0 ? blockedSeq - 1 : pageContiguousSeq
26805
+ );
26806
+ let ackSeq = pageContigBefore;
26807
+ let contigAdvanced = false;
26808
+ if (ownsCursor) {
26809
+ try {
26810
+ if (commitTarget > pageContigBefore) {
26811
+ client._seqTracker.forceContiguousSeq(ns, commitTarget);
26812
+ if (effectiveFloor > pageContigBefore) {
26813
+ client._clientLog.info(`group.v2.pull floor advance: ns=${ns} contiguous=${pageContigBefore} -> effective_floor=${effectiveFloor} retention_floor=${retentionFloor} visibility_floor=${visibilityFloor}`);
26814
+ }
26815
+ if (deferredServerCursor > pageContigBefore && pageCount === 1) {
26816
+ client._clientLog.info(`group.v2.pull restore local A from deferred server ACK cursor: group=${gid}, ns=${ns}, contiguous=${pageContigBefore} -> current_seq=${deferredServerCursor}`);
26817
+ }
26818
+ if (seqs.length > 0) {
26819
+ client._clientLog.debug(`group.v2.pull force contiguous: group=${gid}, ns=${ns}, page_max_seq=${pageMaxSeq}, previous=${pageContigBefore}`);
26820
+ }
26360
26821
  }
26361
- if (deferredServerCursor > pageContigBefore && pageCount === 1) {
26362
- client._clientLog.info(`group.v2.pull restore local A from deferred server ACK cursor: group=${gid}, ns=${ns}, contiguous=${pageContigBefore} -> current_seq=${deferredServerCursor}`);
26822
+ ackSeq = client._seqTracker.getContiguousSeq(ns);
26823
+ contigAdvanced = ackSeq !== pageContigBefore;
26824
+ if (contigAdvanced) {
26825
+ await client._commitSeqTrackerState(ns);
26826
+ if (pageCount === 1 && deferredServerCursor > 0) this.clearForwardCursor(ns, ackSeq);
26363
26827
  }
26364
- if (seqs.length > 0) {
26365
- client._clientLog.debug(`group.v2.pull force contiguous: group=${gid}, ns=${ns}, page_max_seq=${pageMaxSeq}, previous=${pageContigBefore}`);
26828
+ } catch (exc) {
26829
+ if (typeof client._seqTracker.restoreNamespaceSnapshot === "function") {
26830
+ client._seqTracker.restoreNamespaceSnapshot(ns, pageTrackerSnapshot);
26366
26831
  }
26832
+ client._clientLog.error?.(`group.v2.pull Forward commit rolled back: group=${gid} ns=${ns} target=${commitTarget} error=${formatE2EEError(exc)}`);
26833
+ throw exc;
26367
26834
  }
26368
- ackSeq = client._seqTracker.getContiguousSeq(ns);
26369
- contigAdvanced = ackSeq !== pageContigBefore;
26370
- if (contigAdvanced) {
26371
- await client._commitSeqTrackerState(ns);
26372
- if (pageCount === 1 && deferredServerCursor > 0) this.clearForwardCursor(ns, ackSeq);
26373
- }
26374
- } catch (exc) {
26375
- if (typeof client._seqTracker.restoreNamespaceSnapshot === "function") {
26376
- client._seqTracker.restoreNamespaceSnapshot(ns, pageTrackerSnapshot);
26835
+ if (contigAdvanced) await client._drainOrderedMessages(ns, void 0, false, false);
26836
+ }
26837
+ for (const fetch2 of deferredKeyFetches.values()) {
26838
+ this.scheduleSenderIKFetch(fetch2.fromAid, fetch2.senderDeviceId, fetch2.groupId);
26839
+ }
26840
+ client._delivery.onPullPage?.(
26841
+ ns,
26842
+ decrypted.length - pageDecryptedBefore,
26843
+ hasMore,
26844
+ pageMaxSeq,
26845
+ typeof result.remaining === "number" && Number.isSafeInteger(result.remaining) && result.remaining >= 0 ? result.remaining : null,
26846
+ messages.length,
26847
+ client._seqTracker.getContiguousSeq(ns),
26848
+ pageMaxSeq
26849
+ );
26850
+ if (ownsCursor && parsedCursorCurrentSeq !== null && cursorCurrentSeq > ackSeq) {
26851
+ this.recordForwardCursor(ns, cursorCurrentSeq, ackSeq);
26852
+ }
26853
+ const knownServerCursorSeq = hasServerCursor ? cursorCurrentSeq : ownsCursor && pageCount === 1 ? deferredServerCursor : 0;
26854
+ const ackNeeded = ackSeq > 0 && ackSeq > lastAutoAckSeq && ownsCursor && (hasServerCursor && ackSeq > cursorCurrentSeq || contigAdvanced && ackSeq > knownServerCursorSeq);
26855
+ if (ackNeeded) {
26856
+ this.recordForwardAck(ns, ackSeq);
26857
+ const nextAfter2 = Math.max(pageContiguousSeq, nextAfterSeq);
26858
+ const canContinuePage = blockedSeq <= 0 && messages.length > 0 && nextAfter2 > nextAfterSeq && ownsCursor;
26859
+ if (canContinuePage && !pipelineRunning) {
26860
+ pendingAckSeq = Math.max(pendingAckSeq, ackSeq);
26861
+ lastAutoAckSeq = Math.max(lastAutoAckSeq, ackSeq);
26862
+ } else {
26863
+ await this.confirmForwardGroupAck(gid, ackSeq, groupAid);
26864
+ lastAutoAckSeq = Math.max(lastAutoAckSeq, ackSeq);
26377
26865
  }
26378
- client._clientLog.error?.(`group.v2.pull Forward commit rolled back: group=${gid} ns=${ns} target=${commitTarget} error=${formatE2EEError(exc)}`);
26379
- throw exc;
26380
26866
  }
26381
- if (contigAdvanced) await client._drainOrderedMessages(ns, void 0, false, false);
26382
- }
26383
- for (const fetch2 of deferredKeyFetches.values()) {
26384
- this.scheduleSenderIKFetch(fetch2.fromAid, fetch2.senderDeviceId, fetch2.groupId);
26385
- }
26386
- client._delivery.onPullPage?.(
26387
- ns,
26388
- decrypted.length - pageDecryptedBefore,
26389
- hasMore,
26390
- pageMaxSeq,
26391
- typeof result.remaining === "number" && Number.isSafeInteger(result.remaining) && result.remaining >= 0 ? result.remaining : null,
26392
- messages.length,
26393
- client._seqTracker.getContiguousSeq(ns),
26394
- pageMaxSeq
26395
- );
26396
- if (ownsCursor && parsedCursorCurrentSeq !== null && cursorCurrentSeq > ackSeq) {
26397
- this.recordForwardCursor(ns, cursorCurrentSeq, ackSeq);
26398
- }
26399
- const knownServerCursorSeq = hasServerCursor ? cursorCurrentSeq : ownsCursor && pageCount === 1 ? deferredServerCursor : 0;
26400
- const ackNeeded = ackSeq > 0 && ackSeq > lastAutoAckSeq && ownsCursor && (hasServerCursor && ackSeq > cursorCurrentSeq || contigAdvanced && ackSeq > knownServerCursorSeq);
26401
- if (ackNeeded) {
26402
- this.recordForwardAck(ns, ackSeq);
26403
- const nextAfter2 = Math.max(pageContiguousSeq, nextAfterSeq);
26404
- const canContinuePage = blockedSeq <= 0 && messages.length > 0 && nextAfter2 > nextAfterSeq && ownsCursor;
26405
- if (canContinuePage) {
26406
- pendingAckSeq = Math.max(pendingAckSeq, ackSeq);
26407
- lastAutoAckSeq = Math.max(lastAutoAckSeq, ackSeq);
26408
- } else {
26409
- await this.confirmForwardGroupAck(gid, ackSeq, groupAid);
26410
- lastAutoAckSeq = Math.max(lastAutoAckSeq, ackSeq);
26411
- }
26412
- }
26413
- const nextAfter = Math.max(pageContiguousSeq, nextAfterSeq);
26414
- if (!ownsCursor) break;
26415
- const rawCount = messages.length;
26416
- if (blockedSeq > 0) break;
26417
- const shouldContinue = shouldContinueForwardPage({
26418
- rawCount,
26419
- nextAfterSeq,
26420
- nextAfter,
26421
- limit,
26422
- hasMoreField,
26423
- hasMore,
26424
- serverHead,
26425
- pendingAckSeq,
26426
- observedUpper: typeof client._seqTracker.getMaxSeenSeq === "function" ? client._seqTracker.getMaxSeenSeq(ns) : nextAfter
26427
- });
26428
- if (!shouldContinue) break;
26429
- if (pullGateKey && opts?.gateLocked && client._rpcPipeline?.yieldPullGate) {
26430
- const nextKey = pullGateKeyForClient(client, "group.v2.pull", {
26431
- group_id: gid,
26432
- after_seq: nextAfter,
26433
- force: opts?.force === true,
26867
+ const nextAfter = Math.max(pageContiguousSeq, nextAfterSeq);
26868
+ if (!ownsCursor) break;
26869
+ const rawCount = messages.length;
26870
+ if (blockedSeq > 0) break;
26871
+ const shouldContinue = shouldContinueForwardPage({
26872
+ rawCount,
26873
+ nextAfterSeq,
26874
+ nextAfter,
26875
+ limit,
26876
+ hasMoreField,
26877
+ hasMore,
26878
+ serverHead,
26879
+ pendingAckSeq,
26880
+ observedUpper: typeof client._seqTracker.getMaxSeenSeq === "function" ? client._seqTracker.getMaxSeenSeq(ns) : nextAfter
26881
+ });
26882
+ const shouldContinueForPipeline = shouldContinueForwardPage({
26883
+ rawCount,
26884
+ nextAfterSeq,
26885
+ nextAfter,
26434
26886
  limit,
26435
- _group_cursor_params: cursorParams
26436
- }, `${ns}|forward|after=${nextAfter}|force=${String(Boolean(opts?.force))}|limit=${limit}`);
26437
- await client._rpcPipeline.yieldPullGate(pullGateKey, nextKey, true);
26438
- pullGateKey = nextKey;
26887
+ hasMoreField,
26888
+ hasMore,
26889
+ serverHead,
26890
+ pendingAckSeq: 0,
26891
+ observedUpper: typeof client._seqTracker.getMaxSeenSeq === "function" ? client._seqTracker.getMaxSeenSeq(ns) : nextAfter
26892
+ });
26893
+ if (!shouldContinue) break;
26894
+ if (shouldContinueForPipeline && !pipelineRunning && limit >= V2_PULL_MAX_PAGE_LIMIT) {
26895
+ if (pendingAckSeq > 0) {
26896
+ await this.confirmForwardGroupAck(gid, pendingAckSeq, groupAid);
26897
+ pendingAckSeq = 0;
26898
+ }
26899
+ startPipeline(nextAfter);
26900
+ }
26901
+ if (!pipelineRunning && pullGateKey && opts?.gateLocked && client._rpcPipeline?.yieldPullGate) {
26902
+ const nextKey = pullGateKeyForClient(client, "group.v2.pull", {
26903
+ group_id: gid,
26904
+ after_seq: nextAfter,
26905
+ force: opts?.force === true,
26906
+ limit,
26907
+ _group_cursor_params: cursorParams
26908
+ }, `${ns}|forward|after=${nextAfter}|force=${String(Boolean(opts?.force))}|limit=${limit}`);
26909
+ await client._rpcPipeline.yieldPullGate(pullGateKey, nextKey, true);
26910
+ pullGateKey = nextKey;
26911
+ }
26912
+ const tailDelayMs = v2PullTailDelayMs(rawCount, limit);
26913
+ if (tailDelayMs > 0) await v2Sleep(tailDelayMs);
26914
+ nextAfterSeq = nextAfter;
26439
26915
  }
26440
- const tailDelayMs = v2PullTailDelayMs(rawCount, limit);
26441
- if (tailDelayMs > 0) await v2Sleep(tailDelayMs);
26442
- nextAfterSeq = nextAfter;
26916
+ } finally {
26917
+ await stopPipeline();
26443
26918
  }
26444
26919
  if (pageCount >= maxPages) {
26445
26920
  client._clientLog.warn(`group.v2.pull reached max_pages=${maxPages} group=${gid} after_seq=${nextAfterSeq}`);
@@ -26922,7 +27397,9 @@ var V2E2EECoordinator = class {
26922
27397
  };
26923
27398
  const senderDeviceId = String(aad.from_device ?? "").trim();
26924
27399
  if (senderDeviceId) result.sender_device_id = senderDeviceId;
26925
- result.direction = fromAid && fromAid === client._aid ? "outbound_sync" : "inbound";
27400
+ const direction = String(msg.direction ?? "").trim();
27401
+ if (direction) result.direction = direction;
27402
+ else if (fromAid && fromAid !== client._aid) result.direction = "inbound";
26926
27403
  if (msg.t_server !== void 0) result.t_server = msg.t_server;
26927
27404
  if (msg.device_id !== void 0) result.device_id = msg.device_id;
26928
27405
  attachGatewayProximity(result, msg);
@@ -29292,7 +29769,7 @@ var AgentMdManager = class _AgentMdManager {
29292
29769
  this._snapshotLoads.clear();
29293
29770
  return this._agentMdPath;
29294
29771
  }
29295
- async upload(content) {
29772
+ async upload(content, ownerOperationId) {
29296
29773
  const target = this._ownerAid();
29297
29774
  const current = this._currentAid();
29298
29775
  if (!target || !current) {
@@ -29322,7 +29799,7 @@ var AgentMdManager = class _AgentMdManager {
29322
29799
  }
29323
29800
  const signed = signedResult.data.signed;
29324
29801
  await verifyAgentMdUpload(signed, current.certPem, target);
29325
- const result = await this._uploadHttp(target, signed);
29802
+ const result = await this._uploadHttp(target, signed, ownerOperationId);
29326
29803
  const localEtag = await _AgentMdManager.contentEtag(signed);
29327
29804
  const remoteEtag = String(result.etag ?? "").trim();
29328
29805
  await this.saveRecord(target, {
@@ -29713,7 +30190,7 @@ var AgentMdManager = class _AgentMdManager {
29713
30190
  _url(aid, gatewayUrl) {
29714
30191
  return `${agentMdHttpScheme(gatewayUrl)}://${this._safeAid(aid)}/agent.md`;
29715
30192
  }
29716
- async _uploadHttp(aid, content) {
30193
+ async _uploadHttp(aid, content, ownerOperationId) {
29717
30194
  const gatewayUrl = await this._resolveGateway(aid);
29718
30195
  const token = await this._accessToken(aid, gatewayUrl);
29719
30196
  const viaGatewayHost = this._uploadViaGatewayHost && nodeBuiltin("node:http") !== null;
@@ -29722,6 +30199,8 @@ var AgentMdManager = class _AgentMdManager {
29722
30199
  "Content-Type": "text/markdown; charset=utf-8",
29723
30200
  ...viaGatewayHost ? { Host: this._safeAid(aid) } : {}
29724
30201
  };
30202
+ const operationId = String(ownerOperationId ?? "").trim();
30203
+ if (operationId) headers["X-AUN-Owner-Operation-ID"] = operationId;
29725
30204
  let url = this._url(aid, gatewayUrl);
29726
30205
  if (viaGatewayHost) {
29727
30206
  try {
@@ -30607,6 +31086,7 @@ var _AUNClient = class _AUNClient {
30607
31086
  /** transport 的 meta observer:吸收 gateway 注入的 _meta 字段。失败不影响业务。 */
30608
31087
  async _observeRpcMeta(meta) {
30609
31088
  const groupIndexes = isJsonObject(meta.group_indexes) ? meta.group_indexes : {};
31089
+ this._groupIndexMetaCache.observeRpcMeta(meta, { localAid: this._aid ?? "" });
30610
31090
  for (const groupAid of Object.keys(groupIndexes)) {
30611
31091
  await this._loadGroupIndexCache(groupAid);
30612
31092
  }
@@ -32149,6 +32629,11 @@ ${invitee}` : "";
32149
32629
  if (!paramsHash || !timestamp2 || !sigB64) {
32150
32630
  return false;
32151
32631
  }
32632
+ const signedAt = Number(timestamp2) * 1e3;
32633
+ const certMeta = parseCertMetadata(cached.certPem);
32634
+ if (!/^\d+$/.test(timestamp2) || !Number.isSafeInteger(Number(timestamp2)) || signedAt < certMeta.notBefore.getTime() || signedAt > certMeta.notAfter.getTime()) {
32635
+ return false;
32636
+ }
32152
32637
  const pubKey = await importCertPublicKeyEcdsa(cached.certPem);
32153
32638
  const signData = new TextEncoder().encode(`${method}|${sigAid}|${timestamp2}|${paramsHash}`);
32154
32639
  const sigBytes = base64ToUint8(sigB64);
@@ -33472,7 +33957,8 @@ ${invitee}` : "";
33472
33957
  };
33473
33958
  if (senderDeviceId) result.sender_device_id = senderDeviceId;
33474
33959
  const explicitDirection = String(msg.direction ?? "").trim();
33475
- result.direction = explicitDirection || (fromAid && fromAid === this._aid ? "outbound_sync" : "inbound");
33960
+ if (explicitDirection) result.direction = explicitDirection;
33961
+ else if (fromAid && fromAid !== this._aid) result.direction = "inbound";
33476
33962
  if (msg.device_id !== void 0) result.device_id = msg.device_id;
33477
33963
  attachGatewayProximity2(result, msg);
33478
33964
  attachV2EnvelopeMetadata2(result, e2ee);
@@ -34790,6 +35276,19 @@ function agentMdFailure(exc) {
34790
35276
  }
34791
35277
  return resultErr(NETWORK_ERROR, exc instanceof Error ? exc.message : String(exc), exc);
34792
35278
  }
35279
+ function ownerAgentMd(content, ownerAid, proof) {
35280
+ const marker = content.indexOf("<!-- AUN-SIGNATURE");
35281
+ const payload = marker >= 0 ? content.slice(0, marker) : content;
35282
+ const lines = payload.replace(/\r\n/g, "\n").replace(/\r/g, "\n").split("\n");
35283
+ const end = lines.findIndex((line, index) => index > 0 && line.trim() === "---");
35284
+ if (end < 0) throw new ValidationError("agent.md frontmatter is required for ownership operation");
35285
+ const filtered = lines.filter((line, index) => !(index > 0 && index < end && (/^\s*owner\s*:/.test(line) || /^\s*ownership_proof\s*:/.test(line))));
35286
+ const closed = filtered.findIndex((line, index) => index > 0 && line.trim() === "---");
35287
+ filtered.splice(closed, 0, `owner: ${JSON.stringify(ownerAid)}`, `ownership_proof: ${JSON.stringify(proof)}`);
35288
+ const result = filtered.join("\n");
35289
+ return result.endsWith("\n") ? result : `${result}
35290
+ `;
35291
+ }
34793
35292
  var AIDStore = class {
34794
35293
  constructor(opts) {
34795
35294
  __publicField(this, "aunPath");
@@ -34975,7 +35474,7 @@ var AIDStore = class {
34975
35474
  listAsync() {
34976
35475
  return this.list();
34977
35476
  }
34978
- async register(aid) {
35477
+ async registerIdentity(aid, uploadDefault = true) {
34979
35478
  const target = String(aid ?? "").trim();
34980
35479
  try {
34981
35480
  if (!target) throw new ValidationError("AIDStore.register requires 'aid'");
@@ -34993,7 +35492,7 @@ var AIDStore = class {
34993
35492
  });
34994
35493
  }
34995
35494
  await this._persistGatewayUrl(target, gatewayUrl);
34996
- await this.uploadAgentMd(target, buildDefaultAgentMd(target));
35495
+ if (uploadDefault) await this.uploadAgentMd(target, buildDefaultAgentMd(target));
34997
35496
  return resultOk({ registered: true });
34998
35497
  } catch (exc) {
34999
35498
  if (exc instanceof IdentityConflictError) {
@@ -35009,6 +35508,187 @@ var AIDStore = class {
35009
35508
  return resultErr(SERVER_ERROR, message, exc);
35010
35509
  }
35011
35510
  }
35511
+ async register(aidOrParams) {
35512
+ if (typeof aidOrParams === "string") return await this.registerIdentity(aidOrParams);
35513
+ const params2 = aidOrParams;
35514
+ const target = String(params2?.aid ?? "").trim();
35515
+ const aidType = String(params2?.type ?? "").trim().toLowerCase();
35516
+ const owner = String(params2?.owner ?? params2?.owner_aid ?? "").trim();
35517
+ if (!target || !aidType) return resultErr(INVALID_AID_FORMAT, "register requires aid and type");
35518
+ try {
35519
+ const identity = await this.registerIdentity(target, false);
35520
+ if (!identity.ok) return identity;
35521
+ const gatewayUrl = await this._resolveGateway(target);
35522
+ const prepareParams = { aid: target, type: aidType };
35523
+ if (owner) prepareParams.owner = owner;
35524
+ const idempotencyKey = params2.idempotencyKey ?? params2.idempotency_key;
35525
+ if (idempotencyKey) prepareParams.idempotency_key = idempotencyKey;
35526
+ const prepared = await this._registerFlow.shortRpc(gatewayUrl, "auth.aid_register_prepare", prepareParams);
35527
+ const operationId = String(prepared.operation_id ?? "").trim();
35528
+ if (!operationId) throw new ValidationError("aid_register_prepare response missing operation_id");
35529
+ let subjectSignature = params2.subjectSignature ?? params2.subject_signature;
35530
+ const ownerSignature = params2.ownerSignature ?? params2.owner_signature;
35531
+ if (!subjectSignature && owner) {
35532
+ const loaded = await this.load(target);
35533
+ if (loaded.ok) {
35534
+ const statement = OwnershipStatement.create(String(prepared.proof_id ?? ""), target, owner);
35535
+ const signed = await signOwnershipStatement({ certFingerprint: loaded.data.aid.certFingerprint, sign: (payload) => loaded.data.aid.sign(payload) }, statement);
35536
+ subjectSignature = { cert_fingerprint: signed.cert_fingerprint, signature: signed.signature };
35537
+ }
35538
+ }
35539
+ const signatures = {};
35540
+ if (subjectSignature) signatures.subject_signature = subjectSignature;
35541
+ if (ownerSignature) signatures.owner_signature = ownerSignature;
35542
+ let response = prepared;
35543
+ if (Object.keys(signatures).length > 0) response = await this._registerFlow.shortRpc(gatewayUrl, "auth.aid_register_submit", { operation_id: operationId, ...signatures });
35544
+ const state = String(response.state ?? response.operation_state ?? "").toLowerCase();
35545
+ const complete = !owner || owner === target || !!ownerSignature && !!subjectSignature;
35546
+ if (complete && state === "agent_md_pending") {
35547
+ let content = params2.agentMd ?? params2.agent_md ?? buildDefaultAgentMd(target, { type: aidType });
35548
+ if (owner) {
35549
+ const proof = { proof_id: prepared.proof_id, subject_aid: target, owner_aid: owner, fact_hash: prepared.fact_hash, statement_hash: prepared.statement_hash, subject_signature: subjectSignature };
35550
+ if (owner !== target) proof.owner_signature = ownerSignature;
35551
+ content = ownerAgentMd(content, owner, proof);
35552
+ }
35553
+ const uploaded = await this.uploadAgentMd(target, content, { ownerOperationId: operationId });
35554
+ if (!uploaded.ok) return uploaded;
35555
+ response = await this._registerFlow.shortRpc(gatewayUrl, "auth.aid_register_activate", { operation_id: operationId });
35556
+ }
35557
+ return resultOk(response);
35558
+ } catch (exc) {
35559
+ if (exc instanceof ValidationError) return resultErr(INVALID_AID_FORMAT, exc.message, exc);
35560
+ return resultErr(NETWORK_ERROR, exc instanceof Error ? exc.message : String(exc), exc);
35561
+ }
35562
+ }
35563
+ async create(params2) {
35564
+ return await this.register(params2);
35565
+ }
35566
+ async bindAid(params2) {
35567
+ const target = String(params2?.aid ?? "").trim();
35568
+ const aidType = String(params2?.type ?? "").trim().toLowerCase();
35569
+ const owner = String(params2?.owner ?? params2?.owner_aid ?? "").trim();
35570
+ if (!target || !aidType) return resultErr(INVALID_AID_FORMAT, "bindAid requires aid and type");
35571
+ try {
35572
+ this._registerFlow.validateAidName(target);
35573
+ const gatewayUrl = await this._resolveGateway(target);
35574
+ const prepareParams = { aid: target, type: aidType };
35575
+ if (owner) prepareParams.owner = owner;
35576
+ const idempotencyKey = params2.idempotencyKey ?? params2.idempotency_key;
35577
+ if (idempotencyKey) prepareParams.idempotency_key = idempotencyKey;
35578
+ const prepared = await this._registerFlow.shortRpc(gatewayUrl, "auth.aid_bind_prepare", prepareParams);
35579
+ const operationId = String(prepared.operation_id ?? "").trim();
35580
+ if (!operationId) throw new ValidationError("aid_bind_prepare response missing operation_id");
35581
+ const signatures = {};
35582
+ let subjectSignature = params2.subjectSignature ?? params2.subject_signature;
35583
+ if (!subjectSignature && owner) {
35584
+ const loaded = await this.load(target);
35585
+ if (loaded.ok) {
35586
+ const statement = OwnershipStatement.create(String(prepared.proof_id ?? ""), target, owner);
35587
+ const signed = await signOwnershipStatement(loaded.data.aid, statement);
35588
+ subjectSignature = { cert_fingerprint: signed.cert_fingerprint, signature: signed.signature };
35589
+ }
35590
+ }
35591
+ if (subjectSignature) signatures.subject_signature = subjectSignature;
35592
+ const ownerSignature = params2.ownerSignature ?? params2.owner_signature;
35593
+ if (ownerSignature) signatures.owner_signature = ownerSignature;
35594
+ let response = prepared;
35595
+ if (Object.keys(signatures).length > 0) {
35596
+ response = await this._registerFlow.shortRpc(gatewayUrl, "auth.aid_bind_submit", { operation_id: operationId, ...signatures });
35597
+ }
35598
+ const agentMd = params2.agentMd ?? params2.agent_md;
35599
+ if (agentMd !== void 0) {
35600
+ const uploaded = await this.uploadAgentMd(target, agentMd, {
35601
+ ownerOperationId: operationId
35602
+ });
35603
+ if (!uploaded.ok) return uploaded;
35604
+ }
35605
+ const state = String(response.state ?? response.operation_state ?? "").toLowerCase();
35606
+ if (state === "agent_md_pending" || !Object.keys(signatures).length && !owner && String(prepared.state ?? "") === "agent_md_pending") {
35607
+ response = await this._registerFlow.shortRpc(gatewayUrl, "auth.aid_bind_activate", { operation_id: operationId });
35608
+ }
35609
+ return resultOk(response);
35610
+ } catch (exc) {
35611
+ if (exc instanceof ValidationError) return resultErr(INVALID_AID_FORMAT, exc.message, exc);
35612
+ return resultErr(NETWORK_ERROR, exc instanceof Error ? exc.message : String(exc), exc);
35613
+ }
35614
+ }
35615
+ async getAidBindingStatus(aid) {
35616
+ const target = String(aid ?? "").trim();
35617
+ if (!target) return resultErr(INVALID_AID_FORMAT, "getAidBindingStatus requires aid");
35618
+ try {
35619
+ this._registerFlow.validateAidName(target);
35620
+ const gatewayUrl = await this._resolveGateway(target);
35621
+ return resultOk(await this._registerFlow.shortRpc(gatewayUrl, "auth.aid_bind_status", { aid: target }));
35622
+ } catch (exc) {
35623
+ return resultErr(NETWORK_ERROR, exc instanceof Error ? exc.message : String(exc), exc);
35624
+ }
35625
+ }
35626
+ async resumeAidBinding(operationId) {
35627
+ const target = String(operationId ?? "").trim();
35628
+ if (!target) return resultErr(INVALID_AID_FORMAT, "resumeAidBinding requires operationId");
35629
+ try {
35630
+ const gatewayUrl = await this._resolveGateway(target);
35631
+ let response = await this._registerFlow.shortRpc(gatewayUrl, "auth.aid_operation_get", { operation_id: target });
35632
+ if (String(response.state ?? response.operation_state ?? "").toLowerCase() === "agent_md_pending") {
35633
+ const method = String(response.kind ?? "").toLowerCase() === "owner_change" ? "auth.owner_change_activate" : "auth.aid_bind_activate";
35634
+ response = await this._registerFlow.shortRpc(gatewayUrl, method, { operation_id: target });
35635
+ }
35636
+ return resultOk(response);
35637
+ } catch (exc) {
35638
+ return resultErr(NETWORK_ERROR, exc instanceof Error ? exc.message : String(exc), exc);
35639
+ }
35640
+ }
35641
+ async bind_aid(params2) {
35642
+ return this.bindAid(params2);
35643
+ }
35644
+ async get_aid_binding_status(aid) {
35645
+ return this.getAidBindingStatus(aid);
35646
+ }
35647
+ async resume_aid_binding(operationId) {
35648
+ return this.resumeAidBinding(operationId);
35649
+ }
35650
+ async changeOwner(params2) {
35651
+ const subject = String(params2?.subject_aid ?? params2?.aid ?? "").trim();
35652
+ const newOwner = String(params2?.new_owner_aid ?? params2?.new_owner ?? "").trim();
35653
+ if (!subject || !newOwner) return resultErr(INVALID_AID_FORMAT, "changeOwner requires subject_aid and new_owner_aid");
35654
+ try {
35655
+ this._registerFlow.validateAidName(subject);
35656
+ const gatewayUrl = await this._resolveGateway(subject);
35657
+ const prepare = { subject_aid: subject, new_owner_aid: newOwner };
35658
+ if (params2.current_owner_aid ?? params2.owner_aid) prepare.current_owner_aid = params2.current_owner_aid ?? params2.owner_aid;
35659
+ if (params2.expected_relation_version !== void 0) prepare.expected_relation_version = params2.expected_relation_version;
35660
+ if (params2.idempotencyKey ?? params2.idempotency_key) prepare.idempotency_key = params2.idempotencyKey ?? params2.idempotency_key;
35661
+ const prepared = await this._registerFlow.shortRpc(gatewayUrl, "auth.owner_change_prepare", prepare);
35662
+ const operationId = String(prepared.operation_id ?? "").trim();
35663
+ if (!operationId) throw new ValidationError("owner_change_prepare response missing operation_id");
35664
+ const signatures = {};
35665
+ for (const key of ["old_owner_signature", "subject_signature", "new_owner_signature"]) if (params2[key]) signatures[key] = params2[key];
35666
+ let response = prepared;
35667
+ if (Object.keys(signatures).length > 0) response = await this._registerFlow.shortRpc(gatewayUrl, "auth.owner_change_submit", { operation_id: operationId, ...signatures });
35668
+ if (String(response.state ?? response.operation_state ?? "").toLowerCase() === "agent_md_pending" && (params2.agentMd ?? params2.agent_md) !== void 0) {
35669
+ const proof = { proof_id: prepared.proof_id, subject_aid: subject, owner_aid: newOwner, fact_hash: prepared.fact_hash, statement_hash: prepared.statement_hash, subject_signature: params2.subject_signature, owner_signature: params2.new_owner_signature };
35670
+ const content = ownerAgentMd(String(params2.agentMd ?? params2.agent_md), newOwner, proof);
35671
+ const uploaded = await this.uploadAgentMd(subject, content, { ownerOperationId: operationId });
35672
+ if (!uploaded.ok) return uploaded;
35673
+ response = await this._registerFlow.shortRpc(gatewayUrl, "auth.owner_change_activate", { operation_id: operationId });
35674
+ }
35675
+ return resultOk(response);
35676
+ } catch (exc) {
35677
+ return resultErr(NETWORK_ERROR, exc instanceof Error ? exc.message : String(exc), exc);
35678
+ }
35679
+ }
35680
+ async resumeOwnerChange(operationId, aid) {
35681
+ const id = String(operationId ?? "").trim();
35682
+ if (!id) return resultErr(INVALID_AID_FORMAT, "resumeOwnerChange requires operationId");
35683
+ try {
35684
+ const gatewayUrl = await this._resolveGateway(String(aid ?? id));
35685
+ let response = await this._registerFlow.shortRpc(gatewayUrl, "auth.aid_operation_get", { operation_id: id });
35686
+ if (String(response.state ?? response.operation_state ?? "").toLowerCase() === "agent_md_pending") response = await this._registerFlow.shortRpc(gatewayUrl, "auth.owner_change_activate", { operation_id: id });
35687
+ return resultOk(response);
35688
+ } catch (exc) {
35689
+ return resultErr(NETWORK_ERROR, exc instanceof Error ? exc.message : String(exc), exc);
35690
+ }
35691
+ }
35012
35692
  async exists(aid) {
35013
35693
  const target = String(aid ?? "").trim();
35014
35694
  try {
@@ -35135,7 +35815,8 @@ var AIDStore = class {
35135
35815
  accessTokenResolver: async () => this._uploadAgentMdToken(uploader, await this._resolveGateway(uploaderTarget)),
35136
35816
  aidValidator: (value) => this._registerFlow.validateAidName(value)
35137
35817
  });
35138
- return resultOk(await manager.upload(content));
35818
+ const ownerOperationId = options.ownerOperationId ?? options.owner_operation_id;
35819
+ return resultOk(await manager.upload(content, ownerOperationId));
35139
35820
  } catch (exc) {
35140
35821
  return agentMdFailure(exc);
35141
35822
  }
@@ -36694,6 +37375,7 @@ export {
36694
37375
  MessageFacade,
36695
37376
  MessageThoughtFacade,
36696
37377
  NotFoundError,
37378
+ OwnershipStatement,
36697
37379
  PermissionError,
36698
37380
  ProtectedHeaders,
36699
37381
  ROOT_CA_PEM,
@@ -36744,13 +37426,18 @@ export {
36744
37426
  prepareGroupSettingsWithIndex,
36745
37427
  resultErr,
36746
37428
  resultOk,
37429
+ signOwnershipStatement,
36747
37430
  splitGroupId,
36748
37431
  validateAIDFormat,
36749
37432
  validateAgentMdDocument,
36750
37433
  validateGroupAIDFormat,
36751
37434
  validateGroupIDFormat,
37435
+ validateOwnershipFrontmatter,
37436
+ validateOwnershipProof,
36752
37437
  verifyAgentMdUpload,
36753
- verifyGroupIndex
37438
+ verifyGroupIndex,
37439
+ verifyOwnershipProof,
37440
+ verifyOwnershipSignature
36754
37441
  };
36755
37442
  /*! Bundled license information:
36756
37443