@agentunion/fastaun 0.5.33 → 0.5.35

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 (113) hide show
  1. package/CHANGELOG.md +59 -2
  2. package/README.md +28 -26
  3. package/_packed_docs/CHANGELOG.md +59 -2
  4. package/_packed_docs/INDEX.md +26 -12
  5. package/_packed_docs/KITE_DOCS_GUIDE.md +17 -11
  6. package/_packed_docs/agent.md/SCHEMA.md +1 -1
  7. package/_packed_docs/aun/346/265/213/350/257/225/350/277/220/350/241/214/346/214/207/345/215/227.md +2 -2
  8. package/_packed_docs/protocol/03-Gateway-/350/277/236/346/216/245/346/250/241/345/274/217.md +58 -8
  9. package/_packed_docs/protocol/Ownership/345/215/217/350/256/256/345/256/232/344/271/2112.1.md +17 -17
  10. package/_packed_docs/protocol/echo-probe-gateway.md +99 -0
  11. package/_packed_docs/protocol/echo-probe-sdk.md +91 -0
  12. package/_packed_docs/sdk/0.5.33-SDK/345/272/224/347/224/250/351/233/206/346/210/220/346/214/207/345/215/227.md +464 -316
  13. package/_packed_docs/sdk/0.5.33-SDK/345/274/200/345/217/221/344/270/216/347/247/273/346/244/215/346/214/207/345/215/227.md +499 -304
  14. package/_packed_docs/sdk/0.5.34-SDK/345/272/224/347/224/250/351/233/206/346/210/220/346/214/207/345/215/227.md +133 -0
  15. package/_packed_docs/sdk/0.5.34-SDK/345/274/200/345/217/221/344/270/216/347/247/273/346/244/215/346/214/207/345/215/227.md +107 -0
  16. package/_packed_docs/sdk/0.5.35-SDK/345/272/224/347/224/250/351/233/206/346/210/220/346/214/207/345/215/227.md +189 -0
  17. package/_packed_docs/sdk/0.5.35-SDK/345/274/200/345/217/221/344/270/216/347/247/273/346/244/215/346/214/207/345/215/227.md +204 -0
  18. package/_packed_docs/sdk/01-/345/277/253/351/200/237/345/274/200/345/247/213.md +16 -13
  19. package/_packed_docs/sdk/03-/346/240/270/345/277/203/346/246/202/345/277/265.md +13 -1
  20. package/_packed_docs/sdk/04-/350/277/236/346/216/245/344/270/216/350/256/244/350/257/201.md +19 -9
  21. package/_packed_docs/sdk/06-API/346/211/213/345/206/214.md +87 -20
  22. package/_packed_docs/sdk/09-collab-rpc-manual.md +48 -30
  23. package/_packed_docs/sdk/09-custody-api-manual.md +3 -3
  24. package/_packed_docs/sdk/09-group-rpc-manual.md +60 -13
  25. package/_packed_docs/sdk/09-message-rpc-manual.md +22 -0
  26. package/_packed_docs/sdk/09-meta-rpc-manual.md +2 -1
  27. package/_packed_docs/sdk/09-payload-reference.md +9 -8
  28. package/_packed_docs/sdk/09-proxy-rpc-manual.md +20 -14
  29. package/_packed_docs/sdk/09-storage-rpc-manual.md +1698 -1659
  30. package/_packed_docs/sdk/09-stream-rpc-manual.md +33 -20
  31. package/_packed_docs/sdk/AUN_DOCS_GUIDE.md +19 -17
  32. package/_packed_docs/sdk/AUN/344/272/213/344/273/266/345/217/221/345/270/203/350/256/242/351/230/205/345/272/224/347/224/250/351/233/206/346/210/220/346/214/207/345/215/227.md +125 -30
  33. package/_packed_docs/sdk/AUN/344/272/213/344/273/266/345/217/221/345/270/203/350/256/242/351/230/205/345/274/200/345/217/221/347/247/273/346/244/215/346/214/207/345/215/227.md +109 -33
  34. package/_packed_docs/sdk/INDEX.md +30 -18
  35. package/_packed_docs/sdk/Ownership-SDK/347/274/226/346/216/222/346/214/207/345/215/2272.1.md +1 -1
  36. package/_packed_docs/sdk/Ownership2.1-SDK/345/272/224/347/224/250/351/233/206/346/210/220/346/214/207/345/215/227.md +5 -5
  37. package/_packed_docs/sdk/Ownership2.1-SDK/345/274/200/345/217/221/344/270/216/347/247/273/346/244/215/346/214/207/345/215/227.md +5 -5
  38. package/_packed_docs/sdk/Ownership2.1/350/257/246/347/273/206/350/256/276/350/256/241.md +9 -9
  39. package/_packed_docs/sdk/README.md +11 -6
  40. package/_packed_docs//346/266/210/346/201/257/345/274/271/346/200/247/350/241/245/346/213/211/351/200/232/351/201/223/345/256/236/346/226/275/350/256/241/345/210/222.md +400 -0
  41. package/_packed_docs//346/266/210/346/201/257/345/274/271/346/200/247/350/241/245/346/213/211/351/200/232/351/201/223/346/226/271/346/241/210.md +485 -0
  42. package/dist/auth.js +39 -45
  43. package/dist/auth.js.map +1 -1
  44. package/dist/client/control-messages.js +2 -2
  45. package/dist/client/control-messages.js.map +1 -1
  46. package/dist/client/delivery.d.ts +12 -1
  47. package/dist/client/delivery.js +281 -46
  48. package/dist/client/delivery.js.map +1 -1
  49. package/dist/client/lifecycle.js +13 -1
  50. package/dist/client/lifecycle.js.map +1 -1
  51. package/dist/client/rpc-budget.d.ts +21 -0
  52. package/dist/client/rpc-budget.js +82 -0
  53. package/dist/client/rpc-budget.js.map +1 -0
  54. package/dist/client/rpc-pipeline.d.ts +5 -0
  55. package/dist/client/rpc-pipeline.js +227 -63
  56. package/dist/client/rpc-pipeline.js.map +1 -1
  57. package/dist/client/sync-channels.d.ts +90 -0
  58. package/dist/client/sync-channels.js +508 -0
  59. package/dist/client/sync-channels.js.map +1 -0
  60. package/dist/client/v2-e2ee.d.ts +5 -1
  61. package/dist/client/v2-e2ee.js +420 -303
  62. package/dist/client/v2-e2ee.js.map +1 -1
  63. package/dist/client.d.ts +17 -0
  64. package/dist/client.js +373 -50
  65. package/dist/client.js.map +1 -1
  66. package/dist/events.d.ts +1 -0
  67. package/dist/events.js +5 -0
  68. package/dist/events.js.map +1 -1
  69. package/dist/index.d.ts +3 -1
  70. package/dist/index.js +1 -0
  71. package/dist/index.js.map +1 -1
  72. package/dist/mail-client.d.ts +101 -0
  73. package/dist/mail-client.js +369 -0
  74. package/dist/mail-client.js.map +1 -0
  75. package/dist/pubsub/binding.js +23 -3
  76. package/dist/pubsub/binding.js.map +1 -1
  77. package/dist/pubsub/channel.d.ts +15 -3
  78. package/dist/pubsub/channel.js +114 -7
  79. package/dist/pubsub/channel.js.map +1 -1
  80. package/dist/pubsub/client.d.ts +17 -2
  81. package/dist/pubsub/client.js +205 -40
  82. package/dist/pubsub/client.js.map +1 -1
  83. package/dist/pubsub/credentials.d.ts +3 -0
  84. package/dist/pubsub/credentials.js +57 -20
  85. package/dist/pubsub/credentials.js.map +1 -1
  86. package/dist/pubsub/index.d.ts +5 -2
  87. package/dist/pubsub/index.js +70 -54
  88. package/dist/pubsub/index.js.map +1 -1
  89. package/dist/pubsub/service.d.ts +9 -0
  90. package/dist/pubsub/service.js +266 -20
  91. package/dist/pubsub/service.js.map +1 -1
  92. package/dist/pubsub/types.d.ts +13 -1
  93. package/dist/storage/lowlevel.d.ts +3 -1
  94. package/dist/storage/lowlevel.js +13 -0
  95. package/dist/storage/lowlevel.js.map +1 -1
  96. package/dist/storage/types.d.ts +10 -0
  97. package/dist/storage/types.js.map +1 -1
  98. package/dist/storage/vfs.d.ts +3 -1
  99. package/dist/storage/vfs.js +2 -2
  100. package/dist/storage/vfs.js.map +1 -1
  101. package/dist/tools/cross-sdk-agent.d.ts +55 -5
  102. package/dist/tools/cross-sdk-agent.js +88 -21
  103. package/dist/tools/cross-sdk-agent.js.map +1 -1
  104. package/dist/transport.d.ts +16 -0
  105. package/dist/transport.js +129 -7
  106. package/dist/transport.js.map +1 -1
  107. package/dist/udp-standby.d.ts +89 -0
  108. package/dist/udp-standby.js +487 -0
  109. package/dist/udp-standby.js.map +1 -0
  110. package/dist/version.d.ts +1 -1
  111. package/dist/version.js +1 -1
  112. package/package.json +5 -1
  113. package/scripts/patch-imapflow-external.mjs +69 -0
@@ -166,12 +166,16 @@ export class RpcPipeline {
166
166
  inlineRealtimeScopes = new Set();
167
167
  pullInvalidationWait = null;
168
168
  pullDrainedCheckQueued = false;
169
+ backgroundAcks = new Map();
169
170
  constructor(runtime) {
170
171
  this.runtime = runtime;
171
172
  }
172
173
  async call(method, params, assertCurrent, options) {
174
+ const echoStarted = params && this.runtime.client._echoStarts.get(params);
173
175
  if (options?.background !== undefined || options?.deadline !== undefined)
174
176
  params = { ...(params ?? {}), ...(options.background !== undefined ? { _rpc_background: options.background, _rpc_foreground: options.background === false } : {}), ...(options.deadline !== undefined ? { _rpc_deadline: options.deadline } : {}) };
177
+ if (params && echoStarted !== undefined)
178
+ this.runtime.client._echoStarts.set(params, echoStarted);
175
179
  if (method.startsWith('storage.') || method.startsWith('group.fs.')) {
176
180
  const { noCache, no_cache, ...wireParams } = params ?? {};
177
181
  return storageReadCache(this.runtime.client).rpc(method, wireParams, Boolean(noCache ?? no_cache), (requestParams) => this._call(method, requestParams, undefined, assertCurrent));
@@ -183,9 +187,22 @@ export class RpcPipeline {
183
187
  }
184
188
  async _call(method, params, onSent, assertCurrent) {
185
189
  const client = this.runtime.client;
190
+ if ((method === 'message.send' || method === 'group.send') && params && client._isEchoPayload(params.payload ?? params.content)) {
191
+ const started = client._echoStarts.get(params) ?? performance.now();
192
+ params = { ...params };
193
+ client._echoStarts.set(params, started);
194
+ }
186
195
  const aid = client._aid, currentAid = client._currentAid;
187
196
  const generation = client._delivery?.captureInlineAckGeneration?.();
188
197
  const tStart = Date.now();
198
+ const standbyDeadline = Math.min(typeof params?._rpc_deadline === 'number' ? params._rpc_deadline : Infinity, tStart + (Number(client._sessionOptions?.timeouts?.call ?? 35) * 1000));
199
+ const standbyRelease = await client._udpStandby?.acquire(method, standbyDeadline);
200
+ if (client._udpStandby) {
201
+ const started = params && client._echoStarts.get(params);
202
+ params = { ...params, _rpc_deadline: standbyDeadline };
203
+ if (started !== undefined)
204
+ client._echoStarts.set(params, started);
205
+ }
189
206
  const requestedAckSeq = method === 'message.ack' || method === 'message.v2.ack'
190
207
  ? Number(params?.seq
191
208
  ?? params?.up_to_seq
@@ -232,7 +249,7 @@ export class RpcPipeline {
232
249
  const sender = String(p.sender_aid ?? '').trim();
233
250
  if (!sender)
234
251
  throw new ValidationError('peer ACK requires sender_aid');
235
- return await client._v2E2EE.ackPeerV2(sender, requestedAckSeq || undefined, Array.isArray(p.volatile_cursors) ? p.volatile_cursors : [], Array.isArray(p.volatile_ids) ? p.volatile_ids : []);
252
+ return await client._v2E2EE.ackPeerV2(sender, requestedAckSeq || undefined, Array.isArray(p.volatile_cursors) ? p.volatile_cursors : [], Array.isArray(p.volatile_ids) ? p.volatile_ids : [], rpcBackground);
236
253
  }
237
254
  const runWithPullResponseScope = async (operation) => {
238
255
  const scope = pullGateNamespace || (pullGateKey ? this.pullScopeKey(pullGateKey) : '');
@@ -255,6 +272,19 @@ export class RpcPipeline {
255
272
  ? this.runPullNamespaceSerialized.bind(this) : this.runPullSerialized.bind(this);
256
273
  return await run(pullGateKey, async () => this.call(method, lockedParams), pullBackground);
257
274
  }
275
+ if (method === 'message.send' || method === 'group.send') {
276
+ const echo = client._isEchoPayload(p.payload);
277
+ const stopped = client._maybeAppendEchoTraceSend(p);
278
+ if (echo) {
279
+ p.encrypt = false;
280
+ delete p.encrypted;
281
+ }
282
+ if (stopped) {
283
+ const result = { status: 'echo_return', message_id: p.message_id, from: p.to ?? p.group_id ?? client._aid, to: client._aid, ...(p.group_id ? { group_id: p.group_id } : {}), payload: p.payload };
284
+ await client._publishEchoReturn(method, p, result);
285
+ return result;
286
+ }
287
+ }
258
288
  if (method === 'message.send') {
259
289
  const encrypt = p.encrypt ?? true;
260
290
  delete p.encrypt;
@@ -272,7 +302,6 @@ export class RpcPipeline {
272
302
  deadline,
273
303
  }));
274
304
  }
275
- client._maybeAppendEchoTraceSend(p);
276
305
  }
277
306
  if (method === 'group.send') {
278
307
  const encrypt = p.encrypt ?? true;
@@ -291,7 +320,6 @@ export class RpcPipeline {
291
320
  deadline,
292
321
  }));
293
322
  }
294
- client._maybeAppendEchoTraceSend(p);
295
323
  }
296
324
  if (method === 'group.thought.put') {
297
325
  const encrypt = p.encrypt ?? true;
@@ -324,6 +352,7 @@ export class RpcPipeline {
324
352
  gateLocked: true,
325
353
  force,
326
354
  afterVolatileCursor: String(p.after_volatile_cursor ?? '').trim() || undefined,
355
+ background: Boolean(rpcBackground),
327
356
  })));
328
357
  return { messages };
329
358
  }
@@ -415,18 +444,30 @@ export class RpcPipeline {
415
444
  this.applyClientSignature(method, p);
416
445
  assertCurrent?.();
417
446
  const transportParams = deadline === undefined ? p : { ...p, _rpc_deadline: deadline };
418
- const request = onSent
419
- ? client._transport['_callWithSent'](method, transportParams, onSent, remaining, undefined, rpcBackground)
420
- : remaining !== undefined
421
- ? (rpcBackground
422
- ? client._transport.call(method, transportParams, remaining, undefined, rpcBackground)
423
- : client._transport.call(method, transportParams, remaining))
424
- : (rpcBackground
425
- ? client._transport.call(method, transportParams, undefined, undefined, rpcBackground)
426
- : client._transport.call(method, transportParams));
447
+ const history = !onSent && client._historyChannels?.().isHistory(method, transportParams, rpcBackground);
448
+ const request = history
449
+ ? client._historyChannels().call(method, transportParams, remaining, undefined, rpcBackground, (prepared) => this.applyClientSignature(method, prepared))
450
+ : onSent
451
+ ? client._transport['_callWithSent'](method, transportParams, onSent, remaining, undefined, rpcBackground)
452
+ : remaining !== undefined
453
+ ? (rpcBackground
454
+ ? client._transport.call(method, transportParams, remaining, undefined, rpcBackground)
455
+ : client._transport.call(method, transportParams, remaining))
456
+ : (rpcBackground
457
+ ? client._transport.call(method, transportParams, undefined, undefined, rpcBackground)
458
+ : client._transport.call(method, transportParams));
427
459
  return this.bindActivePullCancellation(method, p, request);
428
460
  };
429
- let result = await this.transportCallWithIdentityRecovery(invokeTransport, method, p);
461
+ let result;
462
+ try {
463
+ result = await this.transportCallWithIdentityRecovery(invokeTransport, method, p);
464
+ }
465
+ catch (error) {
466
+ const returned = await client._echoFailure(method, p, error, true);
467
+ if (returned)
468
+ return returned;
469
+ throw error;
470
+ }
430
471
  invalidateStorageReadCache(client, method, p);
431
472
  if (generation === client._delivery?.captureInlineAckGeneration?.()) {
432
473
  if (method === 'group.unblock' && aid && client._aid === aid && client._currentAid === currentAid
@@ -439,6 +480,7 @@ export class RpcPipeline {
439
480
  result = await this.postprocessResult(method, p, result, {
440
481
  skipGroupState: deferGroupReadyPostprocess,
441
482
  pullNamespace: pullGateNamespace,
483
+ background: Boolean(rpcBackground),
442
484
  });
443
485
  if (!skipSendResultEnvelope) {
444
486
  result = client._delivery.attachSendResultEnvelope(method, p, result, Boolean(p.encrypted), sendResultModeSource);
@@ -451,6 +493,7 @@ export class RpcPipeline {
451
493
  throw err;
452
494
  }
453
495
  finally {
496
+ standbyRelease?.();
454
497
  if (generation === client._delivery?.captureInlineAckGeneration?.())
455
498
  client._delivery?.requestBackgroundPullRecovery?.();
456
499
  }
@@ -464,6 +507,8 @@ export class RpcPipeline {
464
507
  throw new PermissionError(`method is internal_only: ${method}`);
465
508
  }
466
509
  const p = { ...(params ?? {}) };
510
+ if (params && client._echoStarts.has(params))
511
+ client._echoStarts.set(p, client._echoStarts.get(params));
467
512
  this.mergeInstanceProtectedHeaders(method, p);
468
513
  const requestedBackground = p._rpc_background ?? p._rpc_background_kind;
469
514
  const rpcBackground = p._rpc_foreground === true ? false
@@ -913,6 +958,19 @@ export class RpcPipeline {
913
958
  return false;
914
959
  return (this.runtime.client._pullResponseKeys.get(this.pullScopeKey(key)) ?? 0) > 0;
915
960
  }
961
+ pendingHistoryNamespaces() {
962
+ const namespaces = new Set();
963
+ for (const state of this.pullGateStates.values())
964
+ for (const job of state.background) {
965
+ if (job.invalidated || job.timedOut || /(?:mode=history|mode=tail|\|tail\||tail=true|volatile=true|force=true|class=(?:control|sdk_control))/.test(job.key))
966
+ continue;
967
+ if ((this.runtime.client._delivery?.automaticPullRetryAt?.(job.namespace, job.operation) ?? 0) > Date.now())
968
+ continue;
969
+ if (job.namespace.startsWith('p2p:') || job.namespace.startsWith('p2p.peer.v1/') || job.namespace.startsWith('group:') || job.namespace.startsWith('group_event:'))
970
+ namespaces.add(job.namespace);
971
+ }
972
+ return [...namespaces];
973
+ }
916
974
  hasPullActivity(namespace, includePending = true) {
917
975
  const ns = String(namespace ?? '').trim();
918
976
  if ((!ns.startsWith('p2p:') && !ns.startsWith('p2p.peer.v1/') && !ns.startsWith('group:') && !ns.startsWith('message_class/')) || this.hasInlineRealtimeGuard(ns))
@@ -1313,6 +1371,98 @@ export class RpcPipeline {
1313
1371
  armQueuedPullTimers(_state) { }
1314
1372
  // ── raw-call ───────────────────────────────────────────────────────────────
1315
1373
  async rawCall(method, params, opts = {}) {
1374
+ const client = this.runtime.client;
1375
+ const deadline = Math.min(typeof params?._rpc_deadline === 'number' ? params._rpc_deadline : Infinity, Date.now() + (opts.timeout ?? Number(client._sessionOptions?.timeouts?.call ?? 35) * 1000));
1376
+ const release = await client._udpStandby?.acquire(method, deadline);
1377
+ try {
1378
+ return await this.rawCallAdmitted(method, client._udpStandby ? { ...params, _rpc_deadline: deadline } : params, opts);
1379
+ }
1380
+ finally {
1381
+ release?.();
1382
+ }
1383
+ }
1384
+ async rawCallAdmitted(method, params, opts = {}) {
1385
+ const client = this.runtime.client;
1386
+ const peerAck = method === 'message.ack' && params?.cursor_scope === 'peer';
1387
+ if (peerAck && params?.seq !== undefined) {
1388
+ params = { ...params, up_to_seq: Object.hasOwn(params, 'up_to_seq') ? params.up_to_seq : params.seq };
1389
+ delete params.seq;
1390
+ }
1391
+ const background = params?._rpc_foreground === true ? false : opts.background ?? params?._rpc_background ?? params?._rpc_background_kind;
1392
+ const field = peerAck ? 'up_to_seq' : { 'message.ack': 'seq', 'message.v2.ack': 'up_to_seq', 'group.ack_messages': 'msg_seq', 'group.v2.ack': 'up_to_seq', 'group.ack_events': 'event_seq' }[method];
1393
+ const seq = field ? params?.[field] : undefined;
1394
+ if (!background || !field || typeof seq !== 'number' || !Number.isSafeInteger(seq) || seq <= 0
1395
+ || Object.keys(params ?? {}).some(key => /volatile|token|proof/.test(key)))
1396
+ return this.rawCallDirect(method, params, opts);
1397
+ const scope = () => [client._aid, client._currentAid, client._deviceId, client._slotId, client._gatewayConnectionId,
1398
+ client._delivery?.captureInlineAckGeneration?.(), client._transport, client._transport?._connectionGeneration, client._transport?._workerGeneration];
1399
+ const identity = scope();
1400
+ const range = { ...params };
1401
+ delete range[field];
1402
+ delete range._rpc_deadline;
1403
+ const key = stableStringify([method, range, opts]);
1404
+ const waitForResponse = (active) => new Promise((resolve, reject) => {
1405
+ let timer;
1406
+ const waiter = { seq, params: { ...params }, resolve: (value) => { clearTimeout(timer); resolve(value); },
1407
+ reject: (error) => { clearTimeout(timer); reject(error); } };
1408
+ active.waiters.push(waiter);
1409
+ const timeout = opts.timeout ?? client._transport?._timeout;
1410
+ const deadline = typeof params?._rpc_deadline === 'number' ? params._rpc_deadline : Infinity;
1411
+ const remaining = Math.min(typeof timeout === 'number' ? timeout : Infinity, deadline - Date.now());
1412
+ if (Number.isFinite(remaining))
1413
+ timer = setTimeout(() => {
1414
+ active.waiters = active.waiters.filter(item => item !== waiter);
1415
+ waiter.reject(new TimeoutError(`rpc timeout: ${method}`, { retryable: true }));
1416
+ }, Math.max(0, remaining));
1417
+ });
1418
+ let job = this.backgroundAcks.get(key);
1419
+ if (job && !job.scope.every((value, index) => value === identity[index]))
1420
+ job = undefined;
1421
+ if (job) {
1422
+ return waitForResponse(job);
1423
+ }
1424
+ job = { scope: identity, params: { ...params }, waiters: [] };
1425
+ const active = job;
1426
+ const response = waitForResponse(active);
1427
+ this.backgroundAcks.set(key, active);
1428
+ const assertCurrent = () => {
1429
+ const currentScope = scope();
1430
+ if (!active.scope.every((value, index) => value === currentScope[index]))
1431
+ throw new ConnectionError('ACK context changed');
1432
+ };
1433
+ void (async () => {
1434
+ try {
1435
+ while (active.waiters.length > 0) {
1436
+ assertCurrent();
1437
+ const latest = active.waiters.reduce((highest, waiter) => waiter.seq > highest.seq ? waiter : highest);
1438
+ const requested = latest.seq;
1439
+ try {
1440
+ const result = await this.rawCallDirect(method, { ...latest.params, [field]: requested }, opts, assertCurrent);
1441
+ const covered = active.waiters.filter(waiter => waiter.seq <= requested);
1442
+ active.waiters = active.waiters.filter(waiter => waiter.seq > requested);
1443
+ for (const waiter of covered)
1444
+ waiter.resolve(result);
1445
+ }
1446
+ catch (error) {
1447
+ const covered = active.waiters.filter(waiter => waiter.seq <= requested);
1448
+ active.waiters = active.waiters.filter(waiter => waiter.seq > requested);
1449
+ for (const waiter of covered)
1450
+ waiter.reject(error);
1451
+ }
1452
+ }
1453
+ }
1454
+ catch (error) {
1455
+ for (const waiter of active.waiters)
1456
+ waiter.reject(error);
1457
+ }
1458
+ finally {
1459
+ if (this.backgroundAcks.get(key) === active)
1460
+ this.backgroundAcks.delete(key);
1461
+ }
1462
+ })();
1463
+ return response;
1464
+ }
1465
+ async rawCallDirect(method, params, opts = {}, assertCurrent) {
1316
1466
  const { timeout, trace, signed = true, background = false, pullGateKey = '' } = opts;
1317
1467
  const client = this.runtime.client;
1318
1468
  if (client._migrationPending)
@@ -1330,8 +1480,11 @@ export class RpcPipeline {
1330
1480
  this.applyClientSignature(method, payload);
1331
1481
  }
1332
1482
  const invokeTransport = () => {
1483
+ assertCurrent?.();
1333
1484
  let request;
1334
- if (rpcBackground)
1485
+ if (client._historyChannels?.().isHistory(method, payload, rpcBackground))
1486
+ request = client._historyChannels().call(method, payload, timeout, trace, rpcBackground, signed ? (prepared) => this.applyClientSignature(method, prepared) : undefined);
1487
+ else if (rpcBackground)
1335
1488
  request = client._transport.call(method, payload, timeout, trace, rpcBackground);
1336
1489
  else if (trace !== undefined)
1337
1490
  request = client._transport.call(method, payload, timeout, trace);
@@ -1364,7 +1517,7 @@ export class RpcPipeline {
1364
1517
  }
1365
1518
  }
1366
1519
  observeBackgroundPullResponse(method, params, result, key = '') {
1367
- if (params.message_class !== undefined && params.message_class !== 'normal' || params.volatile === true)
1520
+ if (params.message_class !== undefined && params.message_class !== 'normal' || params.volatile === true || params.storage_scope === 'volatile')
1368
1521
  return;
1369
1522
  if (!['message.pull', 'message.v2.pull', 'group.pull', 'group.v2.pull', 'group.pull_events'].includes(method)
1370
1523
  || params.force === true || params.window_mode === 'tail')
@@ -1418,59 +1571,70 @@ export class RpcPipeline {
1418
1571
  // ── postprocess ────────────────────────────────────────────────────────────
1419
1572
  async postprocessResult(method, params, result, options = {}) {
1420
1573
  const client = this.runtime.client;
1421
- const pullNamespace = this.pullScopeKey(options.pullNamespace || this.pullGateKeyForCall(method, params));
1422
- this.throwIfPullInvalidated(pullNamespace);
1423
- let next = result;
1424
- if ((method === 'group.send' || method === 'group.v2.send') && isUnknownJsonObject(next)) {
1425
- next = normalizeGroupMentionMode(next);
1426
- }
1427
- if (method === 'group.get_settings' || method === 'group.set_settings') {
1428
- next = normalizeGroupSettingsResponse(next);
1429
- }
1430
- if (method === 'group.thought.get' && isUnknownJsonObject(next)) {
1431
- client._clientLog?.debug?.(`group.thought.get transport result: found=${String(next.found ?? '')}, raw_count=${Array.isArray(next.thoughts) ? next.thoughts.length : 0}`);
1432
- next = await client._decryptGroupThoughts(next);
1433
- }
1434
- if (method === 'message.thought.get' && isUnknownJsonObject(next)) {
1435
- client._clientLog?.debug?.(`message.thought.get transport result: found=${String(next.found ?? '')}, raw_count=${Array.isArray(next.thoughts) ? next.thoughts.length : 0}`);
1436
- next = await client._decryptMessageThoughts(next);
1437
- }
1438
- if (method === 'message.pull' && isUnknownJsonObject(next)) {
1439
- const isTail = String(next.window_mode ?? params.window_mode ?? '').trim().toLowerCase() === 'tail';
1440
- if (!isTail && client._aid && client._seqTracker) {
1441
- const ns = pullNamespace || `p2p:${client._aid}`;
1442
- const afterSeq = Math.max(0, Number(params.after_seq ?? 0) || 0);
1443
- await client._delivery.commitPlainForwardPage(ns, afterSeq, 'message.pull', next);
1444
- }
1445
- else {
1446
- this.postprocessMessagePull(params, next);
1447
- }
1448
- }
1449
- if (method === 'group.pull' && isUnknownJsonObject(next)) {
1450
- if (Array.isArray(next.messages)) {
1451
- next.messages = next.messages.map((message) => normalizeGroupMentionMode(message));
1452
- }
1453
- const gid = String(params.group_id ?? params.group_aid ?? '').trim();
1454
- const explicitCursorParams = this.explicitGroupCursorParams(params);
1455
- const ownsCursor = Object.keys(explicitCursorParams).length === 0
1456
- || this.groupCursorTargetsCurrentInstance(explicitCursorParams);
1457
- const isTail = String(next.window_mode ?? params.window_mode ?? '').trim().toLowerCase() === 'tail';
1458
- if (!ownsCursor) {
1459
- client._clientLog.debug(`group.pull external cursor skips local Forward commit: group=${gid}`);
1574
+ try {
1575
+ const pullNamespace = this.pullScopeKey(options.pullNamespace || this.pullGateKeyForCall(method, params));
1576
+ this.throwIfPullInvalidated(pullNamespace);
1577
+ let next = result;
1578
+ if ((method === 'message.send' || method === 'group.send') && next && typeof next === 'object'
1579
+ && next.status === 'echo_return') {
1580
+ await client._publishEchoReturn(method, params, next);
1581
+ return next;
1582
+ }
1583
+ if ((method === 'group.send' || method === 'group.v2.send') && isUnknownJsonObject(next)) {
1584
+ next = normalizeGroupMentionMode(next);
1585
+ }
1586
+ if (method === 'group.get_settings' || method === 'group.set_settings') {
1587
+ next = normalizeGroupSettingsResponse(next);
1588
+ }
1589
+ if (method === 'group.thought.get' && isUnknownJsonObject(next)) {
1590
+ client._clientLog?.debug?.(`group.thought.get transport result: found=${String(next.found ?? '')}, raw_count=${Array.isArray(next.thoughts) ? next.thoughts.length : 0}`);
1591
+ next = await client._decryptGroupThoughts(next);
1592
+ }
1593
+ if (method === 'message.thought.get' && isUnknownJsonObject(next)) {
1594
+ client._clientLog?.debug?.(`message.thought.get transport result: found=${String(next.found ?? '')}, raw_count=${Array.isArray(next.thoughts) ? next.thoughts.length : 0}`);
1595
+ next = await client._decryptMessageThoughts(next);
1596
+ }
1597
+ if (method === 'message.pull' && isUnknownJsonObject(next)) {
1598
+ const isTail = String(next.window_mode ?? params.window_mode ?? '').trim().toLowerCase() === 'tail';
1599
+ if (!isTail && client._aid && client._seqTracker) {
1600
+ const ns = pullNamespace || `p2p:${client._aid}`;
1601
+ const afterSeq = Math.max(0, Number(params.after_seq ?? 0) || 0);
1602
+ await client._delivery.commitPlainForwardPage(ns, afterSeq, 'message.pull', next, '', options.background === true);
1603
+ }
1604
+ else {
1605
+ this.postprocessMessagePull(params, next);
1606
+ }
1460
1607
  }
1461
- else if (!isTail && gid && client._seqTracker) {
1462
- const ns = pullNamespace || `group:${gid}`;
1463
- const afterSeq = Math.max(0, Number(params.after_message_seq ?? params.after_seq ?? 0) || 0);
1464
- await client._delivery.commitPlainForwardPage(ns, afterSeq, 'group.pull', next, gid);
1608
+ if (method === 'group.pull' && isUnknownJsonObject(next)) {
1609
+ if (Array.isArray(next.messages)) {
1610
+ next.messages = next.messages.map((message) => normalizeGroupMentionMode(message));
1611
+ }
1612
+ const gid = String(params.group_id ?? params.group_aid ?? '').trim();
1613
+ const explicitCursorParams = this.explicitGroupCursorParams(params);
1614
+ const ownsCursor = Object.keys(explicitCursorParams).length === 0
1615
+ || this.groupCursorTargetsCurrentInstance(explicitCursorParams);
1616
+ const isTail = String(next.window_mode ?? params.window_mode ?? '').trim().toLowerCase() === 'tail';
1617
+ if (!ownsCursor) {
1618
+ client._clientLog.debug(`group.pull external cursor skips local Forward commit: group=${gid}`);
1619
+ }
1620
+ else if (!isTail && gid && client._seqTracker) {
1621
+ const ns = pullNamespace || `group:${gid}`;
1622
+ const afterSeq = Math.max(0, Number(params.after_message_seq ?? params.after_seq ?? 0) || 0);
1623
+ await client._delivery.commitPlainForwardPage(ns, afterSeq, 'group.pull', next, gid, options.background === true);
1624
+ }
1625
+ else {
1626
+ this.postprocessGroupPull(params, next);
1627
+ }
1465
1628
  }
1466
- else {
1467
- this.postprocessGroupPull(params, next);
1629
+ if (!options.skipGroupState) {
1630
+ next = await client._groupState.postprocessResult(method, params, next);
1468
1631
  }
1632
+ return next;
1469
1633
  }
1470
- if (!options.skipGroupState) {
1471
- next = await client._groupState.postprocessResult(method, params, next);
1634
+ finally {
1635
+ if (method === 'message.pull' || method === 'group.pull')
1636
+ client._syncChannelCoordinator?.releaseResponse(result);
1472
1637
  }
1473
- return next;
1474
1638
  }
1475
1639
  // ── private helpers ────────────────────────────────────────────────────────
1476
1640
  postprocessMessagePull(params, result) {