@agentunion/fastaun-browser 0.4.11 → 0.4.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/bundle.js CHANGED
@@ -454,7 +454,7 @@ var init_indexeddb_store = __esm({
454
454
  });
455
455
 
456
456
  // src/version.ts
457
- var VERSION = "0.4.11";
457
+ var VERSION = "0.4.12";
458
458
 
459
459
  // src/types.ts
460
460
  var ConnectionState = /* @__PURE__ */ ((ConnectionState2) => {
@@ -3710,6 +3710,53 @@ var ClientRuntime = class {
3710
3710
  var PUSHED_SEQS_LIMIT = 5e4;
3711
3711
  var PENDING_ORDERED_LIMIT = 5e4;
3712
3712
  var GROUP_RECALL_SEEN_LIMIT = 1e4;
3713
+ var APP_MESSAGE_ENVELOPE_KEYS = [
3714
+ "module_id",
3715
+ "message_id",
3716
+ "id",
3717
+ "seq",
3718
+ "msg_seq",
3719
+ "message_type",
3720
+ "type",
3721
+ "kind",
3722
+ "version",
3723
+ "from",
3724
+ "from_aid",
3725
+ "sender_aid",
3726
+ "to",
3727
+ "to_aid",
3728
+ "group_id",
3729
+ "timestamp",
3730
+ "created_at",
3731
+ "t_server",
3732
+ "encrypted",
3733
+ "status",
3734
+ "delivery_mode",
3735
+ "dispatch_mode",
3736
+ "dispatch",
3737
+ "device_id",
3738
+ "slot_id"
3739
+ ];
3740
+ var APP_GROUP_EVENT_ENVELOPE_KEYS = [
3741
+ "module_id",
3742
+ "event_id",
3743
+ "event_seq",
3744
+ "seq",
3745
+ "event_type",
3746
+ "action",
3747
+ "group_id",
3748
+ "actor_aid",
3749
+ "sender_aid",
3750
+ "member_aid",
3751
+ "target_aid",
3752
+ "operator_aid",
3753
+ "created_at",
3754
+ "timestamp",
3755
+ "t_server",
3756
+ "status",
3757
+ "device_id",
3758
+ "slot_id"
3759
+ ];
3713
3760
  function formatDeliveryError(error) {
3714
3761
  return error instanceof Error ? error : String(error);
3715
3762
  }
@@ -3792,8 +3839,46 @@ var MessageDeliveryEngine = class {
3792
3839
  return result;
3793
3840
  }
3794
3841
  normalizePublishedMessagePayload(event, payload) {
3795
- if (!this.isInstanceScopedMessageEvent(event)) return payload;
3796
- return this.stripInternalSenderDeviceFields(this.attachCurrentInstanceContext(payload));
3842
+ if (this.isInstanceScopedMessageEvent(event)) {
3843
+ return this.attachAppMessageEnvelope(this.stripInternalSenderDeviceFields(this.attachCurrentInstanceContext(payload)));
3844
+ }
3845
+ if (this.isGroupScopedEvent(event)) {
3846
+ return this.attachAppGroupEventEnvelope(this.attachCurrentInstanceContext(payload));
3847
+ }
3848
+ return payload;
3849
+ }
3850
+ appMessageEnvelope(payload) {
3851
+ if (!isJsonObject(payload)) return {};
3852
+ const message = payload;
3853
+ const envelope = {};
3854
+ for (const key of APP_MESSAGE_ENVELOPE_KEYS) {
3855
+ if (Object.prototype.hasOwnProperty.call(message, key)) envelope[key] = message[key];
3856
+ }
3857
+ return envelope;
3858
+ }
3859
+ isGroupScopedEvent(event) {
3860
+ return event === "group.changed";
3861
+ }
3862
+ appGroupEventEnvelope(payload) {
3863
+ if (!isJsonObject(payload)) return {};
3864
+ const groupEvent = payload;
3865
+ const envelope = {};
3866
+ for (const key of APP_GROUP_EVENT_ENVELOPE_KEYS) {
3867
+ if (Object.prototype.hasOwnProperty.call(groupEvent, key)) envelope[key] = groupEvent[key];
3868
+ }
3869
+ return envelope;
3870
+ }
3871
+ attachAppMessageEnvelope(payload) {
3872
+ if (!isJsonObject(payload)) return payload;
3873
+ const result = { ...payload };
3874
+ result.envelope = this.appMessageEnvelope(result);
3875
+ return result;
3876
+ }
3877
+ attachAppGroupEventEnvelope(payload) {
3878
+ if (!isJsonObject(payload)) return payload;
3879
+ const result = { ...payload };
3880
+ result.envelope = this.appGroupEventEnvelope(result);
3881
+ return result;
3797
3882
  }
3798
3883
  stripInternalSenderDeviceFields(payload) {
3799
3884
  if (!isJsonObject(payload)) return payload;
@@ -3813,6 +3898,11 @@ var MessageDeliveryEngine = class {
3813
3898
  const payloadType = String(payload.type ?? payload.kind ?? "").trim();
3814
3899
  if (msgType !== "message.recalled" && payloadType !== "message.recalled") return null;
3815
3900
  const event = { ...payload };
3901
+ for (const key of APP_MESSAGE_ENVELOPE_KEYS) {
3902
+ if (Object.prototype.hasOwnProperty.call(msg, key) && !(key in event)) {
3903
+ event[key] = msg[key];
3904
+ }
3905
+ }
3816
3906
  const rawIds = event.message_ids;
3817
3907
  let messageIds = Array.isArray(rawIds) ? rawIds.map((item) => String(item ?? "").trim()).filter(Boolean) : [];
3818
3908
  if (messageIds.length === 0) {
@@ -3831,7 +3921,10 @@ var MessageDeliveryEngine = class {
3831
3921
  if (!("to" in event)) event.to = msg.to ?? msg.to_aid ?? "";
3832
3922
  if (!("timestamp" in event)) event.timestamp = msg.timestamp ?? msg.t_server ?? event.recalled_at ?? 0;
3833
3923
  if ("seq" in msg && !("seq" in event)) event.seq = msg.seq;
3834
- if ("message_id" in msg && !("tombstone_message_id" in event)) event.tombstone_message_id = msg.message_id;
3924
+ if ("message_id" in msg) {
3925
+ event.message_id = msg.message_id;
3926
+ if (!("tombstone_message_id" in event)) event.tombstone_message_id = msg.message_id;
3927
+ }
3835
3928
  if ("device_id" in msg && !("device_id" in event)) event.device_id = msg.device_id;
3836
3929
  if ("slot_id" in msg && !("slot_id" in event)) event.slot_id = msg.slot_id;
3837
3930
  return event;
@@ -3850,6 +3943,11 @@ var MessageDeliveryEngine = class {
3850
3943
  const payloadType = String(payload.type ?? payload.kind ?? "").trim();
3851
3944
  if (msgType !== "group.message_recalled" && payloadType !== "group.message_recalled") return null;
3852
3945
  const event = { ...payload };
3946
+ for (const key of APP_MESSAGE_ENVELOPE_KEYS) {
3947
+ if (Object.prototype.hasOwnProperty.call(msg, key) && !(key in event)) {
3948
+ event[key] = msg[key];
3949
+ }
3950
+ }
3853
3951
  const rawIds = event.message_ids;
3854
3952
  let messageIds = Array.isArray(rawIds) ? rawIds.map((item) => String(item ?? "").trim()).filter(Boolean) : [];
3855
3953
  if (messageIds.length === 0) {
@@ -3867,7 +3965,10 @@ var MessageDeliveryEngine = class {
3867
3965
  if (!("group_id" in event)) event.group_id = msg.group_id ?? "";
3868
3966
  if (!("timestamp" in event)) event.timestamp = msg.timestamp ?? msg.t_server ?? event.recalled_at ?? 0;
3869
3967
  if ("seq" in msg) event.seq = msg.seq;
3870
- if ("message_id" in msg && !("tombstone_message_id" in event)) event.tombstone_message_id = msg.message_id;
3968
+ if ("message_id" in msg) {
3969
+ event.message_id = msg.message_id;
3970
+ if (!("tombstone_message_id" in event)) event.tombstone_message_id = msg.message_id;
3971
+ }
3871
3972
  return event;
3872
3973
  }
3873
3974
  groupRecallDedupKey(groupId, payload) {
@@ -4357,6 +4458,14 @@ var MessageDeliveryEngine = class {
4357
4458
  return;
4358
4459
  }
4359
4460
  const ns = `group_event:${groupId}`;
4461
+ if (this.isSelfJoinGroupChanged(data)) {
4462
+ const contig = client._seqTracker.getContiguousSeq(ns);
4463
+ const maxSeen = client._seqTracker.getMaxSeenSeq(ns);
4464
+ if (contig === 0 && maxSeen === 0 && eventSeq > 1) {
4465
+ client._clientLog.debug(`group.changed self-join baseline: group=${groupId}, event_seq=${eventSeq}, baseline=${eventSeq - 1}`);
4466
+ client._seqTracker.forceContiguousSeq(ns, eventSeq - 1);
4467
+ }
4468
+ }
4360
4469
  const contigBefore = client._seqTracker.getContiguousSeq(ns);
4361
4470
  if (eventSeq <= contigBefore || client._pushedSeqs.get(ns)?.has(eventSeq)) {
4362
4471
  client._clientLog.debug(`group.changed skipped duplicate/stale: group=${groupId}, event_seq=${eventSeq}, contiguous=${contigBefore}`);
@@ -4382,6 +4491,16 @@ var MessageDeliveryEngine = class {
4382
4491
  client._safeAsync(this.fillGroupEventGap(groupId));
4383
4492
  }
4384
4493
  }
4494
+ isSelfJoinGroupChanged(data) {
4495
+ const action = String(data.action ?? "").trim();
4496
+ if (!["member_added", "joined", "join_approved", "invite_code_used"].includes(action)) return false;
4497
+ const selfAid = String(this.runtime.client._aid ?? "").trim();
4498
+ if (!selfAid) return false;
4499
+ const joinedAid = String(data.joined_aid ?? data.member_aid ?? data.aid ?? "").trim();
4500
+ if (joinedAid === selfAid) return true;
4501
+ const actorAid = String(data.actor_aid ?? "").trim();
4502
+ return !joinedAid && ["joined", "invite_code_used"].includes(action) && actorAid === selfAid;
4503
+ }
4385
4504
  enqueueOnlineUnreadHint(data) {
4386
4505
  const client = this.runtime.client;
4387
4506
  const groupId = String(data.group_id ?? "").trim();
@@ -5482,7 +5601,8 @@ var RpcPipeline = class {
5482
5601
  if (!client._instanceProtectedHeaders || !PROTECTED_HEADERS_METHODS.has(method)) {
5483
5602
  return;
5484
5603
  }
5485
- const existing = isJsonObject(params.protected_headers) ? params.protected_headers : {};
5604
+ const existingValue = params.protected_headers ?? params.headers;
5605
+ const existing = isJsonObject(existingValue) ? existingValue : {};
5486
5606
  params.protected_headers = { ...client._instanceProtectedHeaders, ...existing };
5487
5607
  }
5488
5608
  normalizeOutboundMessagePayload(params, method = "") {
@@ -16684,6 +16804,21 @@ var _AUNClient = class _AUNClient {
16684
16804
  if (!this._sessionParams) {
16685
16805
  throw new StateError("missing connect params for reconnect");
16686
16806
  }
16807
+ {
16808
+ const identity = this._identity;
16809
+ if (identity) {
16810
+ const cachedToken = String(identity.access_token ?? "");
16811
+ const expiresAt = this._auth.getAccessTokenExpiry(identity);
16812
+ if (cachedToken && (expiresAt === null || expiresAt > Date.now() / 1e3 + 30)) {
16813
+ this._sessionParams.access_token = cachedToken;
16814
+ } else {
16815
+ this._clientLog.debug(`reconnect: cached token expired or missing for aid=${this._aid ?? ""}, clearing to trigger re-login`);
16816
+ this._sessionParams.access_token = "";
16817
+ }
16818
+ } else {
16819
+ this._sessionParams.access_token = "";
16820
+ }
16821
+ }
16687
16822
  await this._connectOnce(this._sessionParams, true);
16688
16823
  this._lastError = null;
16689
16824
  this._lastErrorCode = null;
@@ -19249,6 +19384,8 @@ var ServiceProxyClient = class {
19249
19384
  const stats = { connection_mode: mode, connections: 0, registered: 0, handled_requests: 0, wakeup_count: 0 };
19250
19385
  try {
19251
19386
  if (mode === "persistent") {
19387
+ const maxReconnectDelay = 6e4;
19388
+ let _delay = Math.max(0, opts.reconnectDelaySeconds ?? 1) * 1e3;
19252
19389
  while (this._running) {
19253
19390
  try {
19254
19391
  await this._autoRegisterServicesWithGateway();
@@ -19261,10 +19398,21 @@ var ServiceProxyClient = class {
19261
19398
  stats.connections = Number(stats.connections) + 1;
19262
19399
  stats.registered = Number(result.registered ?? stats.registered);
19263
19400
  stats.handled_requests = Number(stats.handled_requests) + Number(result.handled_requests ?? 0);
19401
+ _delay = Math.max(0, opts.reconnectDelaySeconds ?? 1) * 1e3;
19264
19402
  } catch (exc) {
19265
19403
  if (!this._running) break;
19266
- this._logWarn(`persistent tunnel reconnect scheduled after error: ${formatError(exc)}`);
19267
- await sleep(Math.max(0, opts.reconnectDelaySeconds ?? 1) * 1e3);
19404
+ if (exc instanceof AuthError) {
19405
+ this._logWarn(`persistent tunnel auth error, re-authenticating: ${formatError(exc)}`);
19406
+ try {
19407
+ await this._authenticateForAccessToken();
19408
+ } catch (reAuthExc) {
19409
+ this._logWarn(`re-authentication failed: ${formatError(reAuthExc)}`);
19410
+ }
19411
+ } else {
19412
+ this._logWarn(`persistent tunnel reconnect scheduled after error: ${formatError(exc)}`);
19413
+ }
19414
+ await sleep(_delay);
19415
+ _delay = Math.min(_delay * 2, maxReconnectDelay);
19268
19416
  } finally {
19269
19417
  this._activeTunnel?.close();
19270
19418
  this._activeTunnel = null;
@@ -13,6 +13,11 @@ export declare class MessageDeliveryEngine {
13
13
  isInstanceScopedMessageEvent(event: string): boolean;
14
14
  attachCurrentInstanceContext(payload: EventPayload): EventPayload;
15
15
  normalizePublishedMessagePayload(event: string, payload: EventPayload): EventPayload;
16
+ appMessageEnvelope(payload: EventPayload): JsonObject;
17
+ isGroupScopedEvent(event: string): boolean;
18
+ appGroupEventEnvelope(payload: EventPayload): JsonObject;
19
+ attachAppMessageEnvelope(payload: EventPayload): EventPayload;
20
+ attachAppGroupEventEnvelope(payload: EventPayload): EventPayload;
16
21
  stripInternalSenderDeviceFields(payload: EventPayload): EventPayload;
17
22
  recallEventFromMessage(message: EventPayload): JsonObject | null;
18
23
  p2pAppEventForMessage(message: EventPayload): {
@@ -34,6 +39,7 @@ export declare class MessageDeliveryEngine {
34
39
  fillGroupGap(groupId: string): Promise<void>;
35
40
  fillGroupEventGap(groupId: string): Promise<void>;
36
41
  handleGroupChangedEventSeq(data: JsonObject, groupId: string): Promise<void>;
42
+ isSelfJoinGroupChanged(data: JsonObject): boolean;
37
43
  enqueueOnlineUnreadHint(data: JsonObject): void;
38
44
  drainOnlineUnreadHints(): Promise<void>;
39
45
  onRawGroupV2MessageCreated(data: EventPayload): Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"delivery.d.ts","sourceRoot":"","sources":["../../src/client/delivery.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAEjD,OAAO,EAAgB,KAAK,UAAU,EAAkB,KAAK,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,aAAa,CAAC;AAC1G,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAUlD,qBAAa,qBAAqB;IAChC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAgB;gBAE5B,OAAO,EAAE,aAAa;IAIlC,eAAe,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAUjC,gBAAgB,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI;IAc/C,qBAAqB,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,GAAG,IAAI;IAc1F,qBAAqB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO;IAIpC,uBAAuB,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IASrG,0BAA0B,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IAatE,4BAA4B,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO;IASpD,4BAA4B,CAAC,OAAO,EAAE,YAAY,GAAG,YAAY;IAajE,gCAAgC,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,GAAG,YAAY;IAKpF,+BAA+B,CAAC,OAAO,EAAE,YAAY,GAAG,YAAY;IAUpE,sBAAsB,CAAC,OAAO,EAAE,YAAY,GAAG,UAAU,GAAG,IAAI;IAmChE,qBAAqB,CAAC,OAAO,EAAE,YAAY,GAAG;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,YAAY,CAAA;KAAE;IAMtF,2BAA2B,CAAC,OAAO,EAAE,YAAY,GAAG,UAAU,GAAG,IAAI;IAgCrE,mBAAmB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,GAAG,MAAM;IAa3D,2BAA2B,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,OAAO,CAAC;IAwBnG,yBAAyB,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IAmE5D,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IAuB1E,6BAA6B,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO;IAkB7D,oBAAoB,CAAC,IAAI,EAAE,YAAY,GAAG,IAAI;IAOxC,wBAAwB,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IA+DjE,wBAAwB,CAAC,IAAI,EAAE,YAAY,GAAG,IAAI;IAO5C,6BAA6B,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IA2FhE,qBAAqB,CAAC,YAAY,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAwD3D,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IA0B3B,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA2B5C,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAyFjD,0BAA0B,CAAC,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAwClF,uBAAuB,CAAC,IAAI,EAAE,UAAU,GAAG,IAAI;IAazC,sBAAsB,IAAI,OAAO,CAAC,IAAI,CAAC;IA0BvC,0BAA0B,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IAkE7D,oBAAoB,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IAmGvD,sBAAsB,IAAI,OAAO,CAAC,IAAI,CAAC;IA0CvC,uBAAuB,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAiE7F,mBAAmB,IAAI,IAAI;IA+C3B,kBAAkB,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IA0BpC,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM;IAgB3E,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,GAAG,SAAS;IA2BtD,oBAAoB,CAAC,EAAE,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAkBnE,qBAAqB,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,OAAO,CAAC;IA+BvG,oBAAoB,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,OAAO,CAAC;CAmB7G"}
1
+ {"version":3,"file":"delivery.d.ts","sourceRoot":"","sources":["../../src/client/delivery.ts"],"names":[],"mappings":"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"}
@@ -4,6 +4,17 @@ import { isJsonObject } from '../types.js';
4
4
  const PUSHED_SEQS_LIMIT = 50_000;
5
5
  const PENDING_ORDERED_LIMIT = 50_000;
6
6
  const GROUP_RECALL_SEEN_LIMIT = 10_000;
7
+ const APP_MESSAGE_ENVELOPE_KEYS = [
8
+ 'module_id', 'message_id', 'id', 'seq', 'msg_seq', 'message_type', 'type', 'kind', 'version',
9
+ 'from', 'from_aid', 'sender_aid', 'to', 'to_aid', 'group_id',
10
+ 'timestamp', 'created_at', 't_server', 'encrypted', 'status', 'delivery_mode',
11
+ 'dispatch_mode', 'dispatch', 'device_id', 'slot_id',
12
+ ];
13
+ const APP_GROUP_EVENT_ENVELOPE_KEYS = [
14
+ 'module_id', 'event_id', 'event_seq', 'seq', 'event_type', 'action', 'group_id',
15
+ 'actor_aid', 'sender_aid', 'member_aid', 'target_aid', 'operator_aid',
16
+ 'created_at', 'timestamp', 't_server', 'status', 'device_id', 'slot_id',
17
+ ];
7
18
  function formatDeliveryError(error) {
8
19
  return error instanceof Error ? error : String(error);
9
20
  }
@@ -95,9 +106,54 @@ export class MessageDeliveryEngine {
95
106
  return result;
96
107
  }
97
108
  normalizePublishedMessagePayload(event, payload) {
98
- if (!this.isInstanceScopedMessageEvent(event))
109
+ if (this.isInstanceScopedMessageEvent(event)) {
110
+ return this.attachAppMessageEnvelope(this.stripInternalSenderDeviceFields(this.attachCurrentInstanceContext(payload)));
111
+ }
112
+ if (this.isGroupScopedEvent(event)) {
113
+ return this.attachAppGroupEventEnvelope(this.attachCurrentInstanceContext(payload));
114
+ }
115
+ return payload;
116
+ }
117
+ appMessageEnvelope(payload) {
118
+ if (!isJsonObject(payload))
119
+ return {};
120
+ const message = payload;
121
+ const envelope = {};
122
+ for (const key of APP_MESSAGE_ENVELOPE_KEYS) {
123
+ if (Object.prototype.hasOwnProperty.call(message, key))
124
+ envelope[key] = message[key];
125
+ }
126
+ return envelope;
127
+ }
128
+ isGroupScopedEvent(event) {
129
+ return event === 'group.changed';
130
+ }
131
+ appGroupEventEnvelope(payload) {
132
+ if (!isJsonObject(payload))
133
+ return {};
134
+ const groupEvent = payload;
135
+ const envelope = {};
136
+ for (const key of APP_GROUP_EVENT_ENVELOPE_KEYS) {
137
+ if (Object.prototype.hasOwnProperty.call(groupEvent, key))
138
+ envelope[key] = groupEvent[key];
139
+ }
140
+ return envelope;
141
+ }
142
+ attachAppMessageEnvelope(payload) {
143
+ if (!isJsonObject(payload))
99
144
  return payload;
100
- return this.stripInternalSenderDeviceFields(this.attachCurrentInstanceContext(payload));
145
+ const result = { ...payload };
146
+ // 兼容期保留顶层信封字段;下一个大版本 0.5.* 将移除这些顶层别名,请通过 envelope.* 访问。
147
+ result.envelope = this.appMessageEnvelope(result);
148
+ return result;
149
+ }
150
+ attachAppGroupEventEnvelope(payload) {
151
+ if (!isJsonObject(payload))
152
+ return payload;
153
+ const result = { ...payload };
154
+ // 兼容期保留顶层群事件信封字段;下一个大版本 0.5.* 将移除这些顶层别名,请通过 envelope.* 访问。
155
+ result.envelope = this.appGroupEventEnvelope(result);
156
+ return result;
101
157
  }
102
158
  stripInternalSenderDeviceFields(payload) {
103
159
  if (!isJsonObject(payload))
@@ -120,6 +176,11 @@ export class MessageDeliveryEngine {
120
176
  if (msgType !== 'message.recalled' && payloadType !== 'message.recalled')
121
177
  return null;
122
178
  const event = { ...payload };
179
+ for (const key of APP_MESSAGE_ENVELOPE_KEYS) {
180
+ if (Object.prototype.hasOwnProperty.call(msg, key) && !(key in event)) {
181
+ event[key] = msg[key];
182
+ }
183
+ }
123
184
  const rawIds = event.message_ids;
124
185
  let messageIds = Array.isArray(rawIds)
125
186
  ? rawIds.map((item) => String(item ?? '').trim()).filter(Boolean)
@@ -144,8 +205,11 @@ export class MessageDeliveryEngine {
144
205
  event.timestamp = msg.timestamp ?? msg.t_server ?? event.recalled_at ?? 0;
145
206
  if ('seq' in msg && !('seq' in event))
146
207
  event.seq = msg.seq;
147
- if ('message_id' in msg && !('tombstone_message_id' in event))
148
- event.tombstone_message_id = msg.message_id;
208
+ if ('message_id' in msg) {
209
+ event.message_id = msg.message_id;
210
+ if (!('tombstone_message_id' in event))
211
+ event.tombstone_message_id = msg.message_id;
212
+ }
149
213
  if ('device_id' in msg && !('device_id' in event))
150
214
  event.device_id = msg.device_id;
151
215
  if ('slot_id' in msg && !('slot_id' in event))
@@ -169,6 +233,11 @@ export class MessageDeliveryEngine {
169
233
  if (msgType !== 'group.message_recalled' && payloadType !== 'group.message_recalled')
170
234
  return null;
171
235
  const event = { ...payload };
236
+ for (const key of APP_MESSAGE_ENVELOPE_KEYS) {
237
+ if (Object.prototype.hasOwnProperty.call(msg, key) && !(key in event)) {
238
+ event[key] = msg[key];
239
+ }
240
+ }
172
241
  const rawIds = event.message_ids;
173
242
  let messageIds = Array.isArray(rawIds)
174
243
  ? rawIds.map((item) => String(item ?? '').trim()).filter(Boolean)
@@ -191,8 +260,11 @@ export class MessageDeliveryEngine {
191
260
  event.timestamp = msg.timestamp ?? msg.t_server ?? event.recalled_at ?? 0;
192
261
  if ('seq' in msg)
193
262
  event.seq = msg.seq;
194
- if ('message_id' in msg && !('tombstone_message_id' in event))
195
- event.tombstone_message_id = msg.message_id;
263
+ if ('message_id' in msg) {
264
+ event.message_id = msg.message_id;
265
+ if (!('tombstone_message_id' in event))
266
+ event.tombstone_message_id = msg.message_id;
267
+ }
196
268
  return event;
197
269
  }
198
270
  groupRecallDedupKey(groupId, payload) {
@@ -740,6 +812,14 @@ export class MessageDeliveryEngine {
740
812
  return;
741
813
  }
742
814
  const ns = `group_event:${groupId}`;
815
+ if (this.isSelfJoinGroupChanged(data)) {
816
+ const contig = client._seqTracker.getContiguousSeq(ns);
817
+ const maxSeen = client._seqTracker.getMaxSeenSeq(ns);
818
+ if (contig === 0 && maxSeen === 0 && eventSeq > 1) {
819
+ client._clientLog.debug(`group.changed self-join baseline: group=${groupId}, event_seq=${eventSeq}, baseline=${eventSeq - 1}`);
820
+ client._seqTracker.forceContiguousSeq(ns, eventSeq - 1);
821
+ }
822
+ }
743
823
  const contigBefore = client._seqTracker.getContiguousSeq(ns);
744
824
  if (eventSeq <= contigBefore || client._pushedSeqs.get(ns)?.has(eventSeq)) {
745
825
  client._clientLog.debug(`group.changed skipped duplicate/stale: group=${groupId}, event_seq=${eventSeq}, contiguous=${contigBefore}`);
@@ -766,6 +846,19 @@ export class MessageDeliveryEngine {
766
846
  client._safeAsync(this.fillGroupEventGap(groupId));
767
847
  }
768
848
  }
849
+ isSelfJoinGroupChanged(data) {
850
+ const action = String(data.action ?? '').trim();
851
+ if (!['member_added', 'joined', 'join_approved', 'invite_code_used'].includes(action))
852
+ return false;
853
+ const selfAid = String(this.runtime.client._aid ?? '').trim();
854
+ if (!selfAid)
855
+ return false;
856
+ const joinedAid = String(data.joined_aid ?? data.member_aid ?? data.aid ?? '').trim();
857
+ if (joinedAid === selfAid)
858
+ return true;
859
+ const actorAid = String(data.actor_aid ?? '').trim();
860
+ return !joinedAid && ['joined', 'invite_code_used'].includes(action) && actorAid === selfAid;
861
+ }
769
862
  enqueueOnlineUnreadHint(data) {
770
863
  const client = this.runtime.client;
771
864
  const groupId = String(data.group_id ?? '').trim();