@agentunion/fastaun 0.5.8 → 0.5.9

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 (38) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/_packed_docs/CHANGELOG.md +32 -0
  3. package/_packed_docs/agent.md/examples//347/276/244/347/273/204-/345/274/200/345/217/221/345/233/242/351/230/237.md +21 -0
  4. package/dist/agent-md-schema.js +3 -0
  5. package/dist/agent-md-schema.js.map +1 -1
  6. package/dist/aid-store.js +1 -3
  7. package/dist/aid-store.js.map +1 -1
  8. package/dist/auth.d.ts +3 -1
  9. package/dist/auth.js +64 -13
  10. package/dist/auth.js.map +1 -1
  11. package/dist/client/delivery.d.ts +4 -0
  12. package/dist/client/delivery.js +121 -20
  13. package/dist/client/delivery.js.map +1 -1
  14. package/dist/client/group-state.js +2 -2
  15. package/dist/client/group-state.js.map +1 -1
  16. package/dist/client/lifecycle.js +20 -8
  17. package/dist/client/lifecycle.js.map +1 -1
  18. package/dist/client/rpc-pipeline.d.ts +8 -0
  19. package/dist/client/rpc-pipeline.js +150 -32
  20. package/dist/client/rpc-pipeline.js.map +1 -1
  21. package/dist/client/v2-e2ee.js +12 -4
  22. package/dist/client/v2-e2ee.js.map +1 -1
  23. package/dist/client.js +87 -11
  24. package/dist/client.js.map +1 -1
  25. package/dist/discovery.d.ts +1 -0
  26. package/dist/discovery.js +3 -0
  27. package/dist/discovery.js.map +1 -1
  28. package/dist/facades.js +7 -3
  29. package/dist/facades.js.map +1 -1
  30. package/dist/index.d.ts +1 -1
  31. package/dist/index.js.map +1 -1
  32. package/dist/net.d.ts +3 -0
  33. package/dist/net.js +13 -0
  34. package/dist/net.js.map +1 -1
  35. package/dist/version.d.ts +1 -1
  36. package/dist/version.js +1 -1
  37. package/package.json +1 -1
  38. package/_packed_docs//345/217/221/345/270/203/346/212/245/345/221/212-0.5.6.md +0 -260
@@ -67,6 +67,8 @@ export class MessageDeliveryEngine {
67
67
  realtimeTailHeads = null;
68
68
  realtimeSyncing = null;
69
69
  pendingGroupPullUpper = null;
70
+ onlineUnreadHintTargets = null;
71
+ onlineUnreadHintTasks = null;
70
72
  pendingP2PInlineAcks = null;
71
73
  pendingGroupInlineAcks = null;
72
74
  pendingGroupInlineAckNamespaces = null;
@@ -76,6 +78,20 @@ export class MessageDeliveryEngine {
76
78
  constructor(runtime) {
77
79
  this.runtime = runtime;
78
80
  }
81
+ isPullOperationCurrent() {
82
+ const client = this.runtime.client;
83
+ const generation = client._pullOperationGeneration;
84
+ if (generation === undefined)
85
+ return true;
86
+ const pipeline = client._rpcPipeline;
87
+ return typeof pipeline?.isPullGenerationCurrent === 'function'
88
+ ? pipeline.isPullGenerationCurrent(generation)
89
+ : true;
90
+ }
91
+ ensurePullOperationCurrent() {
92
+ if (!this.isPullOperationCurrent())
93
+ throw new Error('pull invalidated');
94
+ }
79
95
  forwardCoordinator() {
80
96
  const coordinator = this.runtime.client._v2E2EE;
81
97
  if (!coordinator
@@ -91,6 +107,7 @@ export class MessageDeliveryEngine {
91
107
  }
92
108
  async confirmPlainForwardAck(ns, method, ackSeq, groupId = '') {
93
109
  const client = this.runtime.client;
110
+ this.ensurePullOperationCurrent();
94
111
  const coordinator = this.forwardCoordinator();
95
112
  const generation = this.captureInlineAckGeneration();
96
113
  coordinator.recordForwardAck(ns, ackSeq);
@@ -109,6 +126,7 @@ export class MessageDeliveryEngine {
109
126
  _rpc_background: true,
110
127
  };
111
128
  const result = await client._rpcPipeline.rawCall(method, params, { background: true });
129
+ this.ensurePullOperationCurrent();
112
130
  const actualAckSeq = this.resolveForwardAckSeq(result, ackSeq);
113
131
  if (actualAckSeq < ackSeq) {
114
132
  throw new Error(`${method} server ACK watermark ${actualAckSeq} is below requested ${ackSeq}`);
@@ -126,6 +144,7 @@ export class MessageDeliveryEngine {
126
144
  throw new Error(`${method} response must be an object`);
127
145
  }
128
146
  const client = this.runtime.client;
147
+ this.ensurePullOperationCurrent();
129
148
  const response = result;
130
149
  const messages = deduplicatePlainForwardPageMessages(response.messages, afterSeq);
131
150
  const coordinator = this.forwardCoordinator();
@@ -156,25 +175,30 @@ export class MessageDeliveryEngine {
156
175
  let committed = false;
157
176
  try {
158
177
  for (const rawMessage of messages) {
178
+ this.ensurePullOperationCurrent();
159
179
  const seq = positiveSafeSequenceHint(rawMessage.seq);
160
180
  if (method === 'message.pull') {
161
181
  const appEvent = this.p2pAppEventForMessage(rawMessage);
162
182
  if (await this.publishPulledMessage(appEvent.event, ns, seq, appEvent.payload, false)) {
163
183
  publishedCount += 1;
164
184
  }
185
+ this.ensurePullOperationCurrent();
165
186
  continue;
166
187
  }
167
188
  const message = normalizeGroupMentionMode(rawMessage);
168
189
  if (this.recallEventFromGroupMessage(message)) {
169
190
  if (await this.publishGroupRecallTombstone(groupId, seq, message)) {
191
+ this.ensurePullOperationCurrent();
170
192
  this.markPublishedSeq(ns, seq);
171
193
  publishedCount += 1;
172
194
  }
173
195
  }
174
196
  else if (await this.publishPulledMessage('group.message_created', ns, seq, message, false)) {
197
+ this.ensurePullOperationCurrent();
175
198
  publishedCount += 1;
176
199
  }
177
200
  }
201
+ this.ensurePullOperationCurrent();
178
202
  if (messages.length > 0)
179
203
  client._seqTracker.onPullResult(ns, messages, afterSeq);
180
204
  const commitTarget = Math.max(client._seqTracker.getContiguousSeq(ns), retentionFloor, visibilityFloor, deferredServerCursor);
@@ -182,18 +206,21 @@ export class MessageDeliveryEngine {
182
206
  client._seqTracker.forceContiguousSeq(ns, commitTarget);
183
207
  }
184
208
  if (client._seqTracker.getContiguousSeq(ns) !== pageContigBefore) {
209
+ this.ensurePullOperationCurrent();
185
210
  await this.drainOrderedMessages(ns, undefined, false, false);
211
+ this.ensurePullOperationCurrent();
186
212
  await client._commitSeqTrackerState(ns);
187
213
  }
188
214
  committed = true;
189
215
  }
190
216
  catch (exc) {
191
- if (!committed && typeof client._seqTracker.restoreNamespaceSnapshot === 'function') {
217
+ if (this.isPullOperationCurrent() && !committed && typeof client._seqTracker.restoreNamespaceSnapshot === 'function') {
192
218
  client._seqTracker.restoreNamespaceSnapshot(ns, pageTrackerSnapshot);
193
219
  this.dropSeqTrackerPending(ns);
194
220
  }
195
221
  throw exc;
196
222
  }
223
+ this.ensurePullOperationCurrent();
197
224
  const committedAck = client._seqTracker.getContiguousSeq(ns);
198
225
  if (deferredServerCursor > 0 && committedAck >= deferredServerCursor) {
199
226
  coordinator.clearForwardCursor(ns, committedAck);
@@ -224,17 +251,21 @@ export class MessageDeliveryEngine {
224
251
  if (clampedAckSeq < pendingAckSeq) {
225
252
  throw new Error(`${ackMethod} cannot confirm pending Forward watermark ${pendingAckSeq}`);
226
253
  }
254
+ this.ensurePullOperationCurrent();
227
255
  await this.confirmPlainForwardAck(ns, ackMethod, pendingAckSeq, groupId);
228
256
  }
229
257
  return { rawCount: messages.length, publishedCount };
230
258
  }
231
259
  resetInlineAckState() {
232
260
  this.inlineAckGeneration += 1;
261
+ void this.runtime.client._rpcPipeline?.invalidatePulls?.();
233
262
  this.pendingP2PInlineAcks = null;
234
263
  this.pendingGroupInlineAcks = null;
235
264
  this.pendingGroupInlineAckNamespaces = null;
236
265
  this.realtimeSyncing = null;
237
266
  this.pendingGroupPullUpper = null;
267
+ this.onlineUnreadHintTargets = null;
268
+ this.onlineUnreadHintTasks = null;
238
269
  this.realtimeTailHeads = null;
239
270
  }
240
271
  inlineGenerationIsCurrent(generation) {
@@ -366,12 +397,15 @@ export class MessageDeliveryEngine {
366
397
  }
367
398
  async publishOrderedQueueItem(ns, event, seq, payload, source, pullResponse = false) {
368
399
  const client = this.runtime.client;
400
+ this.ensurePullOperationCurrent();
369
401
  if (event === 'group.changed' && this.isGroupEventNamespace(ns)) {
370
402
  await this.publishOrderedGroupChanged(payload, source);
403
+ this.ensurePullOperationCurrent();
371
404
  return;
372
405
  }
373
406
  if (event === 'message.recalled') {
374
407
  await this.publishMessageRecallTombstone(seq, payload);
408
+ this.ensurePullOperationCurrent();
375
409
  return;
376
410
  }
377
411
  if (pullResponse) {
@@ -384,6 +418,7 @@ export class MessageDeliveryEngine {
384
418
  if (isPromiseLike(published))
385
419
  await published;
386
420
  }
421
+ this.ensurePullOperationCurrent();
387
422
  }
388
423
  async publishOrderedGroupChanged(payload, source = 'ordered') {
389
424
  const client = this.runtime.client;
@@ -1938,6 +1973,18 @@ export class MessageDeliveryEngine {
1938
1973
  raw = response.effective_ack_seq;
1939
1974
  else if (Object.prototype.hasOwnProperty.call(response, 'ack_seq'))
1940
1975
  raw = response.ack_seq;
1976
+ else if (Object.prototype.hasOwnProperty.call(response, 'cursor')) {
1977
+ const cursor = response.cursor;
1978
+ const cursorObject = isJsonObject(cursor) ? cursor : null;
1979
+ if (cursorObject) {
1980
+ if (!Object.prototype.hasOwnProperty.call(cursorObject, 'current_seq'))
1981
+ return 0;
1982
+ raw = cursorObject.current_seq;
1983
+ }
1984
+ else {
1985
+ raw = cursor;
1986
+ }
1987
+ }
1941
1988
  else
1942
1989
  return requestedSeq;
1943
1990
  return typeof raw === 'number' && Number.isSafeInteger(raw) && raw >= 0 ? raw : 0;
@@ -2020,19 +2067,20 @@ export class MessageDeliveryEngine {
2020
2067
  const state = this.syncState(ns);
2021
2068
  const needsForward = state.ack < state.tail - 1;
2022
2069
  const backgroundEnabled = client._sessionOptions?.background_sync !== false;
2023
- // Tail 期间到达的新 Push 可能只更新 maxSeen 而没有扩展 Tail 窗口;
2024
- // 仍须在本次 Tail 周期内补一页 Forward,不能交给外层重复恢复。
2025
- const tailMissedDuringPull = tailHead !== null
2026
- && Number(client._seqTracker.getMaxSeenSeq?.(ns) ?? 0) > state.head;
2070
+ // Tail 期间到达的新 Push 可能只更新 maxSeen 而没有扩展 Tail 窗口。
2071
+ const maxSeen = Number(client._seqTracker.getMaxSeenSeq?.(ns) ?? 0);
2072
+ const tailMissedDuringPull = tailHead !== null && maxSeen > state.head;
2027
2073
  const tailForward = tailHead !== null && (needsForward || tailMissedDuringPull);
2028
2074
  const backgroundWindowForward = (order.length === 1 && order[0] === 'forward' && !headForward)
2029
2075
  || (order.length === 1 && order[0] === 'tail' && tailHead === null && needsForward);
2030
2076
  const foregroundForward = headForward || tailForward;
2031
2077
  if (foregroundForward || backgroundEnabled && backgroundWindowForward) {
2078
+ const forwardTarget = Math.max(headForward ? pushSeq : 0, tailForward || backgroundWindowForward ? state.tail - 1 : 0, tailMissedDuringPull ? maxSeen : 0);
2079
+ const requiredPages = Math.max(1, Math.ceil(Math.max(0, forwardTarget - state.ack) / pageLimit));
2032
2080
  result = await run({
2033
2081
  after_seq: state.ack,
2034
2082
  limit: pageLimit,
2035
- ...(headForward ? { max_pages: 2 } : order[0] === 'tail' ? { max_pages: 1 } : {}),
2083
+ ...(headForward ? { max_pages: Math.max(2, requiredPages) } : order[0] === 'tail' ? { max_pages: requiredPages } : {}),
2036
2084
  }, !foregroundForward, headForward || tailMissedDuringPull);
2037
2085
  }
2038
2086
  if (tailHead !== null && this.syncState(ns).ack < this.syncState(ns).tail - 1) {
@@ -2094,18 +2142,19 @@ export class MessageDeliveryEngine {
2094
2142
  const state = this.syncState(ns);
2095
2143
  const needsForward = state.ack < state.tail - 1;
2096
2144
  const backgroundEnabled = client._sessionOptions?.background_sync !== false;
2097
- // P2P 对称:Tail 期间出现的新 Push 在本次周期内补一页 Forward。
2098
- const tailMissedDuringPull = tailHead !== null
2099
- && Number(client._seqTracker.getMaxSeenSeq?.(ns) ?? 0) > state.head;
2145
+ const maxSeen = Number(client._seqTracker.getMaxSeenSeq?.(ns) ?? 0);
2146
+ const tailMissedDuringPull = tailHead !== null && maxSeen > state.head;
2100
2147
  const tailForward = tailHead !== null && (needsForward || tailMissedDuringPull);
2101
2148
  const backgroundWindowForward = (order.length === 1 && order[0] === 'forward' && !headForward)
2102
2149
  || (order.length === 1 && order[0] === 'tail' && tailHead === null && needsForward);
2103
2150
  const foregroundForward = headForward || tailForward;
2104
2151
  if (foregroundForward || backgroundEnabled && backgroundWindowForward) {
2152
+ const forwardTarget = Math.max(headForward ? pushSeq : 0, tailForward || backgroundWindowForward ? state.tail - 1 : 0, tailMissedDuringPull ? maxSeen : 0);
2153
+ const requiredPages = Math.max(1, Math.ceil(Math.max(0, forwardTarget - state.ack) / pageLimit));
2105
2154
  result = await run({
2106
2155
  after_seq: state.ack,
2107
2156
  limit: pageLimit,
2108
- ...(headForward ? { max_pages: 2 } : order[0] === 'tail' ? { max_pages: 1 } : {}),
2157
+ ...(headForward ? { max_pages: Math.max(2, requiredPages) } : order[0] === 'tail' ? { max_pages: requiredPages } : {}),
2109
2158
  }, !foregroundForward, headForward || tailMissedDuringPull);
2110
2159
  }
2111
2160
  if (tailHead !== null && this.syncState(ns).ack < this.syncState(ns).tail - 1) {
@@ -2129,12 +2178,14 @@ export class MessageDeliveryEngine {
2129
2178
  const key = client._rpcPipeline.pullGateKeyForCall('message.pull', request);
2130
2179
  return await client._rpcPipeline.runPullSerialized(key, invoke, background);
2131
2180
  }
2132
- async runGroupForwardRecovery(groupId, ns, pageLimit, background = false) {
2181
+ async runGroupForwardRecovery(groupId, ns, pageLimit, background = false, throughSeq = 0) {
2133
2182
  const client = this.runtime.client;
2134
- const request = { group_id: groupId, after_seq: this.syncState(ns).ack, limit: pageLimit, max_pages: 1 };
2183
+ const ack = this.syncState(ns).ack;
2184
+ const maxPages = Math.max(1, Math.ceil(Math.max(0, throughSeq - ack) / pageLimit));
2185
+ const request = { group_id: groupId, after_seq: ack, limit: pageLimit, max_pages: maxPages };
2135
2186
  const invoke = async () => {
2136
2187
  const after = this.syncState(ns).ack;
2137
- const messages = await client._pullGroupV2(groupId, after, pageLimit, { gateLocked: true, maxPages: 1 });
2188
+ const messages = await client._pullGroupV2(groupId, after, pageLimit, { gateLocked: true, maxPages });
2138
2189
  return { messages, raw_count: messages.length };
2139
2190
  };
2140
2191
  if (!client._rpcPipeline)
@@ -2770,9 +2821,34 @@ export class MessageDeliveryEngine {
2770
2821
  if (!groupId)
2771
2822
  return;
2772
2823
  const ns = `group:${groupId}`;
2773
- client._safeAsync(this.runGroupForwardRecovery(groupId, ns, 50, true).catch((exc) => {
2774
- client._clientLog.debug(`online unread hint background Forward failed: ns=${ns} err=${formatDeliveryError(exc)}`);
2775
- }));
2824
+ const targets = this.onlineUnreadHintTargets ?? new Map();
2825
+ const tasks = this.onlineUnreadHintTasks ?? new Map();
2826
+ this.onlineUnreadHintTargets = targets;
2827
+ this.onlineUnreadHintTasks = tasks;
2828
+ targets.set(ns, Math.max(targets.get(ns) ?? 0, positiveSafeSequenceHint(data.seq)));
2829
+ if (tasks.has(ns))
2830
+ return;
2831
+ const task = Promise.resolve().then(async () => {
2832
+ try {
2833
+ while (true) {
2834
+ const ackBefore = this.syncState(ns).ack;
2835
+ await this.runGroupForwardRecovery(groupId, ns, 50, true, targets.get(ns) ?? 0);
2836
+ const ackAfter = this.syncState(ns).ack;
2837
+ if (ackAfter >= (targets.get(ns) ?? 0) || ackAfter <= ackBefore)
2838
+ return;
2839
+ }
2840
+ }
2841
+ catch (exc) {
2842
+ client._clientLog.debug(`online unread hint background Forward failed: ns=${ns} err=${formatDeliveryError(exc)}`);
2843
+ }
2844
+ finally {
2845
+ targets.delete(ns);
2846
+ if (tasks.get(ns) === task)
2847
+ tasks.delete(ns);
2848
+ }
2849
+ });
2850
+ tasks.set(ns, task);
2851
+ client._safeAsync(task);
2776
2852
  }
2777
2853
  async onRawGroupV2MessageCreated(data, expectedVersion, inlineGeneration) {
2778
2854
  const client = this.runtime.client;
@@ -3250,11 +3326,13 @@ export class MessageDeliveryEngine {
3250
3326
  }
3251
3327
  async drainOrderedMessages(ns, beforeSeq, pullResponse = false, persist = true) {
3252
3328
  const client = this.runtime.client;
3329
+ this.ensurePullOperationCurrent();
3253
3330
  const queue = client._pendingOrderedMsgs.get(ns);
3254
3331
  if (!queue || queue.size === 0)
3255
3332
  return;
3256
3333
  let delivered = false;
3257
3334
  while (true) {
3335
+ this.ensurePullOperationCurrent();
3258
3336
  const contig = client._seqTracker.getContiguousSeq(ns);
3259
3337
  const ready = [...queue.keys()]
3260
3338
  .filter((seq) => seq <= contig && (beforeSeq === undefined || seq < beforeSeq))
@@ -3280,6 +3358,7 @@ export class MessageDeliveryEngine {
3280
3358
  continue;
3281
3359
  }
3282
3360
  await this.publishOrderedQueueItem(ns, item.event, seq, item.payload, 'ordered-drain', pullResponse);
3361
+ this.ensurePullOperationCurrent();
3283
3362
  this.markPublishedSeq(ns, seq);
3284
3363
  client._markOrderedSeqDelivered(ns, seq);
3285
3364
  delivered = true;
@@ -3287,16 +3366,20 @@ export class MessageDeliveryEngine {
3287
3366
  }
3288
3367
  if (queue.size === 0) {
3289
3368
  client._pendingOrderedMsgs.delete(ns);
3290
- if (delivered && persist)
3369
+ if (delivered && persist) {
3370
+ this.ensurePullOperationCurrent();
3291
3371
  await this.saveSeqTrackerState();
3372
+ }
3292
3373
  }
3293
3374
  }
3294
3375
  async publishOrderedMessage(event, ns, seq, payload) {
3295
3376
  const client = this.runtime.client;
3377
+ this.ensurePullOperationCurrent();
3296
3378
  const seqNum = Number(seq);
3297
3379
  if (!Number.isFinite(seqNum) || !Number.isInteger(seqNum) || seqNum <= 0) {
3298
3380
  client._clientLog.debug(`publish ordered direct(no-seq): event=${event}, ns=${ns || '<none>'}, seq=${String(seq)}`);
3299
3381
  await this.publishOrderedQueueItem(ns, event, seqNum, payload, 'ordered');
3382
+ this.ensurePullOperationCurrent();
3300
3383
  return true;
3301
3384
  }
3302
3385
  if (client._pushedSeqs.get(ns)?.has(seqNum)) {
@@ -3314,6 +3397,7 @@ export class MessageDeliveryEngine {
3314
3397
  return false;
3315
3398
  }
3316
3399
  await this.drainOrderedMessages(ns, seqNum);
3400
+ this.ensurePullOperationCurrent();
3317
3401
  if (client._pushedSeqs.get(ns)?.has(seqNum)) {
3318
3402
  client._clientLog.debug(`publish ordered skipped after-drain duplicate: event=${event}, ns=${ns}, seq=${seqNum}`);
3319
3403
  return false;
@@ -3323,19 +3407,24 @@ export class MessageDeliveryEngine {
3323
3407
  if (queue && queue.size === 0)
3324
3408
  client._pendingOrderedMsgs.delete(ns);
3325
3409
  await this.publishOrderedQueueItem(ns, event, seqNum, payload, 'ordered');
3410
+ this.ensurePullOperationCurrent();
3326
3411
  this.markPublishedSeq(ns, seqNum);
3327
3412
  client._markOrderedSeqDelivered(ns, seqNum);
3328
3413
  client._clientLog.debug(`publish ordered delivered: event=${event}, ns=${ns}, seq=${seqNum}`);
3329
3414
  await this.drainOrderedMessages(ns);
3330
- if (!client._pendingOrderedMsgs.get(ns))
3415
+ if (!client._pendingOrderedMsgs.get(ns)) {
3416
+ this.ensurePullOperationCurrent();
3331
3417
  await this.saveSeqTrackerState();
3418
+ }
3332
3419
  return true;
3333
3420
  }
3334
3421
  async publishOrderedGroupRecall(ns, seq, message) {
3335
3422
  const client = this.runtime.client;
3423
+ this.ensurePullOperationCurrent();
3336
3424
  const seqNum = Number(seq);
3337
3425
  if (!Number.isFinite(seqNum) || !Number.isInteger(seqNum) || seqNum <= 0) {
3338
3426
  await this.publishGroupRecallTombstone(ns.replace(/^group:/, ''), seq, message);
3427
+ this.ensurePullOperationCurrent();
3339
3428
  return true;
3340
3429
  }
3341
3430
  // 撤回 tombstone 也占 seq:推进 contiguous 并标记已发布,确保 ack 正常推进、不留空洞。
@@ -3345,24 +3434,31 @@ export class MessageDeliveryEngine {
3345
3434
  }
3346
3435
  client._seqTracker.onMessageSeq(ns, seqNum);
3347
3436
  await this.publishGroupRecallTombstone(ns.replace(/^group:/, ''), seq, message);
3437
+ this.ensurePullOperationCurrent();
3348
3438
  this.markPublishedSeq(ns, seqNum);
3349
3439
  client._markOrderedSeqDelivered?.(ns, seqNum);
3350
3440
  await this.drainOrderedMessages(ns);
3351
- if (!client._pendingOrderedMsgs.get(ns))
3441
+ if (!client._pendingOrderedMsgs.get(ns)) {
3442
+ this.ensurePullOperationCurrent();
3352
3443
  await this.saveSeqTrackerState();
3444
+ }
3353
3445
  return true;
3354
3446
  }
3355
3447
  async publishPulledMessage(event, ns, seq, payload, persist = true) {
3356
3448
  const client = this.runtime.client;
3449
+ this.ensurePullOperationCurrent();
3357
3450
  const seqNum = Number(seq);
3358
3451
  if (!Number.isFinite(seqNum) || !Number.isInteger(seqNum) || seqNum <= 0 || !ns) {
3359
3452
  client._clientLog.debug(`publish pulled direct(no-seq): event=${event}, ns=${ns || '<none>'}, seq=${String(seq)}`);
3360
3453
  if (event === 'message.recalled') {
3361
- return this.publishMessageRecallTombstone(seq, payload);
3454
+ const published = await this.publishMessageRecallTombstone(seq, payload);
3455
+ this.ensurePullOperationCurrent();
3456
+ return published;
3362
3457
  }
3363
3458
  const published = client._withPullResponseProcessing(ns, () => client._publishAppEvent(event, payload, 'pull'));
3364
3459
  if (isPromiseLike(published))
3365
3460
  await published;
3461
+ this.ensurePullOperationCurrent();
3366
3462
  return true;
3367
3463
  }
3368
3464
  const queue = client._pendingOrderedMsgs.get(ns);
@@ -3374,22 +3470,27 @@ export class MessageDeliveryEngine {
3374
3470
  return false;
3375
3471
  }
3376
3472
  await this.drainOrderedMessages(ns, seqNum, true, persist);
3473
+ this.ensurePullOperationCurrent();
3377
3474
  queue?.delete(seqNum);
3378
3475
  if (queue && queue.size === 0)
3379
3476
  client._pendingOrderedMsgs.delete(ns);
3380
3477
  if (event === 'message.recalled') {
3381
3478
  const recallPublished = await this.publishMessageRecallTombstone(seqNum, payload);
3479
+ this.ensurePullOperationCurrent();
3382
3480
  this.markPublishedSeq(ns, seqNum);
3383
3481
  client._clientLog.debug(`publish pulled delivered: event=${event}, ns=${ns}, seq=${seqNum}`);
3384
3482
  await this.drainOrderedMessages(ns, undefined, true, persist);
3483
+ this.ensurePullOperationCurrent();
3385
3484
  return recallPublished;
3386
3485
  }
3387
3486
  const published = client._withPullResponseProcessing(ns, () => client._publishAppEvent(event, payload, 'pull'));
3388
3487
  if (isPromiseLike(published))
3389
3488
  await published;
3489
+ this.ensurePullOperationCurrent();
3390
3490
  this.markPublishedSeq(ns, seqNum);
3391
3491
  client._clientLog.debug(`publish pulled delivered: event=${event}, ns=${ns}, seq=${seqNum}`);
3392
3492
  await this.drainOrderedMessages(ns, undefined, true, persist);
3493
+ this.ensurePullOperationCurrent();
3393
3494
  return true;
3394
3495
  }
3395
3496
  }