@agentunion/fastaun-browser 0.5.9 → 0.5.11

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 (54) hide show
  1. package/CHANGELOG.md +64 -0
  2. package/_packed_docs/CHANGELOG.md +64 -0
  3. package/_packed_docs/INDEX.md +3 -3
  4. package/_packed_docs/KITE_DOCS_GUIDE.md +1 -1
  5. package/_packed_docs/sdk/04-/350/277/236/346/216/245/344/270/216/350/256/244/350/257/201.md +6 -5
  6. package/_packed_docs/sdk/06-API/346/211/213/345/206/214.md +102 -8
  7. package/_packed_docs/sdk/09-group-rpc-manual.md +18 -3
  8. package/_packed_docs/sdk/09-message-rpc-manual.md +34 -10
  9. package/_packed_docs/sdk/AUN_DOCS_GUIDE.md +3 -2
  10. package/_packed_docs/sdk/INDEX.md +2 -1
  11. package/dist/agent-md.d.ts.map +1 -1
  12. package/dist/agent-md.js +23 -1
  13. package/dist/agent-md.js.map +1 -1
  14. package/dist/auth.d.ts.map +1 -1
  15. package/dist/auth.js +16 -2
  16. package/dist/auth.js.map +1 -1
  17. package/dist/bundle.js +1374 -267
  18. package/dist/client/delivery.d.ts +34 -11
  19. package/dist/client/delivery.d.ts.map +1 -1
  20. package/dist/client/delivery.js +390 -100
  21. package/dist/client/delivery.js.map +1 -1
  22. package/dist/client/group-state.js +6 -6
  23. package/dist/client/group-state.js.map +1 -1
  24. package/dist/client/lifecycle.d.ts.map +1 -1
  25. package/dist/client/lifecycle.js +34 -38
  26. package/dist/client/lifecycle.js.map +1 -1
  27. package/dist/client/rpc-pipeline.d.ts +4 -0
  28. package/dist/client/rpc-pipeline.d.ts.map +1 -1
  29. package/dist/client/rpc-pipeline.js +70 -4
  30. package/dist/client/rpc-pipeline.js.map +1 -1
  31. package/dist/client/v2-e2ee.d.ts +4 -1
  32. package/dist/client/v2-e2ee.d.ts.map +1 -1
  33. package/dist/client/v2-e2ee.js +169 -38
  34. package/dist/client/v2-e2ee.js.map +1 -1
  35. package/dist/client.d.ts +1 -1
  36. package/dist/client.d.ts.map +1 -1
  37. package/dist/client.js +83 -33
  38. package/dist/client.js.map +1 -1
  39. package/dist/events.d.ts +31 -2
  40. package/dist/events.d.ts.map +1 -1
  41. package/dist/events.js +167 -7
  42. package/dist/events.js.map +1 -1
  43. package/dist/register-flow.d.ts.map +1 -1
  44. package/dist/register-flow.js +16 -2
  45. package/dist/register-flow.js.map +1 -1
  46. package/dist/transport.d.ts +10 -1
  47. package/dist/transport.d.ts.map +1 -1
  48. package/dist/transport.js +427 -29
  49. package/dist/transport.js.map +1 -1
  50. package/dist/version.d.ts +1 -1
  51. package/dist/version.d.ts.map +1 -1
  52. package/dist/version.js +1 -1
  53. package/dist/version.js.map +1 -1
  54. package/package.json +1 -1
package/dist/transport.js CHANGED
@@ -4,9 +4,311 @@ import { isJsonObject } from './types.js';
4
4
  const MAX_WS_PAYLOAD_SIZE = 1_000_000;
5
5
  const MAX_RPC_INFLIGHT = 16;
6
6
  const MAX_BACKGROUND_RPC_INFLIGHT = 8;
7
+ const WORKER_START_TIMEOUT_MS = 2_000;
8
+ const WORKER_OPEN_TIMEOUT_MS = 2_000;
7
9
  const _noopLog = { error: () => { }, warn: () => { }, info: () => { }, debug: () => { } };
8
10
  /** RPC 请求递增计数器,避免 Date.now() 高并发碰撞 */
9
11
  let _rpcIdCounter = 0;
12
+ const WORKER_WEBSOCKET_SOURCE = `
13
+ let socket = null;
14
+ self.postMessage({ type: 'ready' });
15
+ self.onmessage = (event) => {
16
+ const command = event.data || {};
17
+ if (command.type === 'connect') {
18
+ try {
19
+ socket = new WebSocket(command.url);
20
+ socket.onopen = () => self.postMessage({ type: 'open' });
21
+ socket.onmessage = (message) => self.postMessage({ type: 'message', data: message.data });
22
+ socket.onerror = () => self.postMessage({ type: 'error', message: 'websocket error' });
23
+ socket.onclose = (close) => self.postMessage({
24
+ type: 'close', code: close.code, reason: close.reason || '', wasClean: close.wasClean === true,
25
+ });
26
+ } catch (error) {
27
+ self.postMessage({ type: 'error', message: error instanceof Error ? error.message : String(error) });
28
+ }
29
+ return;
30
+ }
31
+ if (command.type === 'send') {
32
+ try {
33
+ if (!socket || socket.readyState !== WebSocket.OPEN) throw new Error('websocket is not open');
34
+ socket.send(command.data);
35
+ self.postMessage({ type: 'send_result', id: command.id, ok: true });
36
+ } catch (error) {
37
+ self.postMessage({
38
+ type: 'send_result', id: command.id, ok: false,
39
+ error: error instanceof Error ? error.message : String(error),
40
+ });
41
+ }
42
+ return;
43
+ }
44
+ if (command.type === 'close' && socket) socket.close(command.code, command.reason);
45
+ };
46
+ `;
47
+ class WorkerWebSocketProxy {
48
+ static CONNECTING = 0;
49
+ static OPEN = 1;
50
+ static CLOSING = 2;
51
+ static CLOSED = 3;
52
+ readyState = WorkerWebSocketProxy.CONNECTING;
53
+ onopen = null;
54
+ onmessage = null;
55
+ onerror = null;
56
+ onclose = null;
57
+ _url;
58
+ _worker;
59
+ _workerUrl;
60
+ _nativeSocket = null;
61
+ _workerReady = false;
62
+ _opened = false;
63
+ _closedEventEmitted = false;
64
+ _workerStartTimer = null;
65
+ _workerOpenTimer = null;
66
+ _listeners = new Map();
67
+ _pendingSends = new Map();
68
+ _sendSeq = 0;
69
+ constructor(url) {
70
+ this._url = url;
71
+ this._workerUrl = URL.createObjectURL(new Blob([WORKER_WEBSOCKET_SOURCE], { type: 'text/javascript' }));
72
+ try {
73
+ this._worker = new Worker(this._workerUrl);
74
+ }
75
+ catch (error) {
76
+ URL.revokeObjectURL(this._workerUrl);
77
+ throw error;
78
+ }
79
+ const worker = this._worker;
80
+ worker.onmessage = (event) => this._handleWorkerMessage(event.data);
81
+ worker.onerror = (event) => {
82
+ event.preventDefault?.();
83
+ if (!this._opened) {
84
+ this._fallbackToNative();
85
+ return;
86
+ }
87
+ this._fallbackToNative(true);
88
+ };
89
+ this._workerStartTimer = globalThis.setTimeout(() => this._fallbackToNative(), WORKER_START_TIMEOUT_MS);
90
+ }
91
+ send(data) {
92
+ if (this.readyState !== WorkerWebSocketProxy.OPEN)
93
+ return Promise.reject(new Error('websocket is not open'));
94
+ if (this._nativeSocket) {
95
+ try {
96
+ this._nativeSocket.send(data);
97
+ return Promise.resolve();
98
+ }
99
+ catch (error) {
100
+ return Promise.reject(error instanceof Error ? error : new Error(String(error)));
101
+ }
102
+ }
103
+ const worker = this._worker;
104
+ if (!worker)
105
+ return Promise.reject(new Error('websocket worker is not available'));
106
+ const id = ++this._sendSeq;
107
+ return new Promise((resolve, reject) => {
108
+ this._pendingSends.set(id, { resolve, reject });
109
+ try {
110
+ worker.postMessage({ type: 'send', id, data });
111
+ }
112
+ catch (error) {
113
+ this._pendingSends.delete(id);
114
+ reject(error instanceof Error ? error : new Error(String(error)));
115
+ }
116
+ });
117
+ }
118
+ close(code, reason) {
119
+ if (this.readyState === WorkerWebSocketProxy.CLOSED)
120
+ return;
121
+ this.readyState = WorkerWebSocketProxy.CLOSING;
122
+ if (this._nativeSocket) {
123
+ try {
124
+ this._nativeSocket.close(code, reason);
125
+ }
126
+ catch {
127
+ this._emitClose(new CloseEvent('close', { code: code ?? 1000, reason: reason ?? '', wasClean: true }));
128
+ }
129
+ return;
130
+ }
131
+ if (!this._workerReady || !this._worker) {
132
+ this._disposeWorker();
133
+ this._emitClose(new CloseEvent('close', { code: code ?? 1000, reason: reason ?? '', wasClean: true }));
134
+ return;
135
+ }
136
+ try {
137
+ this._worker.postMessage({ type: 'close', code, reason });
138
+ }
139
+ catch {
140
+ this._fallbackToNative();
141
+ }
142
+ }
143
+ addEventListener(type, listener) {
144
+ const callback = typeof listener === 'function' ? listener : (event) => listener.handleEvent(event);
145
+ const listeners = this._listeners.get(type) ?? new Set();
146
+ listeners.add(callback);
147
+ this._listeners.set(type, listeners);
148
+ }
149
+ removeEventListener(type, listener) {
150
+ if (typeof listener === 'function')
151
+ this._listeners.get(type)?.delete(listener);
152
+ }
153
+ _handleWorkerMessage(reply) {
154
+ if (reply.type === 'ready') {
155
+ if (this._workerReady || !this._worker)
156
+ return;
157
+ this._workerReady = true;
158
+ this._clearWorkerStartTimer();
159
+ try {
160
+ this._worker.postMessage({ type: 'connect', url: this._url });
161
+ this._workerOpenTimer = globalThis.setTimeout(() => this._fallbackToNative(), WORKER_OPEN_TIMEOUT_MS);
162
+ }
163
+ catch {
164
+ this._fallbackToNative();
165
+ }
166
+ return;
167
+ }
168
+ if (reply.type === 'send_result' && reply.id !== undefined) {
169
+ const pending = this._pendingSends.get(reply.id);
170
+ if (!pending)
171
+ return;
172
+ this._pendingSends.delete(reply.id);
173
+ if (reply.ok)
174
+ pending.resolve();
175
+ else
176
+ pending.reject(new Error(reply.error || 'websocket send failed'));
177
+ return;
178
+ }
179
+ if (reply.type === 'open') {
180
+ this._clearWorkerOpenTimer();
181
+ this._opened = true;
182
+ this.readyState = WorkerWebSocketProxy.OPEN;
183
+ const event = new Event('open');
184
+ this.onopen?.(event);
185
+ this._emit('open', event);
186
+ return;
187
+ }
188
+ if (reply.type === 'message') {
189
+ const event = new MessageEvent('message', { data: reply.data });
190
+ this.onmessage?.(event);
191
+ this._emit('message', event);
192
+ return;
193
+ }
194
+ if (reply.type === 'error') {
195
+ if (!this._opened && this.readyState === WorkerWebSocketProxy.CONNECTING) {
196
+ this._fallbackToNative();
197
+ return;
198
+ }
199
+ this._emitError(reply.message || 'websocket worker error');
200
+ return;
201
+ }
202
+ if (reply.type === 'close') {
203
+ if (!this._opened && this.readyState === WorkerWebSocketProxy.CONNECTING) {
204
+ this._fallbackToNative();
205
+ return;
206
+ }
207
+ this._emitClose(new CloseEvent('close', {
208
+ code: reply.code ?? 1006, reason: reply.reason ?? '', wasClean: reply.wasClean === true,
209
+ }));
210
+ }
211
+ }
212
+ _fallbackToNative(force = false) {
213
+ if (this._nativeSocket || this.readyState === WorkerWebSocketProxy.CLOSED)
214
+ return;
215
+ if (!force && (this.readyState !== WorkerWebSocketProxy.CONNECTING || this._opened))
216
+ return;
217
+ const wasOpened = this._opened;
218
+ this._opened = false;
219
+ this.readyState = WorkerWebSocketProxy.CONNECTING;
220
+ this._disposeWorker();
221
+ try {
222
+ const socket = new WebSocket(this._url);
223
+ this._nativeSocket = socket;
224
+ socket.onopen = (event) => {
225
+ this._opened = true;
226
+ this.readyState = WorkerWebSocketProxy.OPEN;
227
+ if (!wasOpened) {
228
+ this.onopen?.(event);
229
+ this._emit('open', event);
230
+ }
231
+ };
232
+ socket.onmessage = (event) => {
233
+ this.onmessage?.(event);
234
+ this._emit('message', event);
235
+ };
236
+ socket.onerror = (event) => {
237
+ this.onerror?.(event);
238
+ this._emit('error', event);
239
+ };
240
+ socket.onclose = (event) => this._emitClose(event);
241
+ }
242
+ catch (error) {
243
+ this.readyState = WorkerWebSocketProxy.CLOSED;
244
+ this._emitError(error instanceof Error ? error.message : String(error));
245
+ }
246
+ }
247
+ _emitClose(event) {
248
+ if (this._closedEventEmitted)
249
+ return;
250
+ this._closedEventEmitted = true;
251
+ this.readyState = WorkerWebSocketProxy.CLOSED;
252
+ for (const pending of this._pendingSends.values())
253
+ pending.reject(new Error('websocket closed'));
254
+ this._pendingSends.clear();
255
+ this.onclose?.(event);
256
+ this._emit('close', event);
257
+ this._disposeWorker();
258
+ this._nativeSocket = null;
259
+ }
260
+ _clearWorkerStartTimer() {
261
+ if (this._workerStartTimer === null)
262
+ return;
263
+ globalThis.clearTimeout(this._workerStartTimer);
264
+ this._workerStartTimer = null;
265
+ }
266
+ _clearWorkerOpenTimer() {
267
+ if (this._workerOpenTimer === null)
268
+ return;
269
+ globalThis.clearTimeout(this._workerOpenTimer);
270
+ this._workerOpenTimer = null;
271
+ }
272
+ _disposeWorker() {
273
+ this._clearWorkerStartTimer();
274
+ this._clearWorkerOpenTimer();
275
+ for (const pending of this._pendingSends.values())
276
+ pending.reject(new Error('websocket worker unavailable'));
277
+ this._pendingSends.clear();
278
+ const worker = this._worker;
279
+ this._worker = null;
280
+ if (worker) {
281
+ worker.onmessage = null;
282
+ worker.onerror = null;
283
+ worker.terminate();
284
+ }
285
+ const workerUrl = this._workerUrl;
286
+ this._workerUrl = null;
287
+ if (workerUrl && typeof URL.revokeObjectURL === 'function')
288
+ URL.revokeObjectURL(workerUrl);
289
+ }
290
+ _emitError(message) {
291
+ const event = new ErrorEvent('error', { message });
292
+ this.onerror?.(event);
293
+ this._emit('error', event);
294
+ }
295
+ _emit(type, event) {
296
+ for (const listener of [...(this._listeners.get(type) ?? [])])
297
+ listener(event);
298
+ }
299
+ }
300
+ export function createTransportWebSocket(url) {
301
+ if (typeof Worker === 'function' && typeof Blob === 'function' && typeof URL !== 'undefined'
302
+ && typeof URL.createObjectURL === 'function' && typeof URL.revokeObjectURL === 'function') {
303
+ try {
304
+ return new WorkerWebSocketProxy(url);
305
+ }
306
+ catch {
307
+ // Worker/CSP 不可用时保持原生 WebSocket 路径。
308
+ }
309
+ }
310
+ return new WebSocket(url);
311
+ }
10
312
  // ── Trace 树状展示 ─────────────────────────────────────────────
11
313
  const TRACE_SPAN_DETAIL_FIELDS = [
12
314
  'method', 'route', 'namespace', 'instance_id', 'aid', 'caller_aid',
@@ -260,6 +562,10 @@ export class RPCTransport {
260
562
  _traceMode = 'off';
261
563
  // Trace observer:observer(traceInfo) 在每次 RPC/事件携带 _trace 时调用
262
564
  _traceObserver = null;
565
+ // 每个 transport 实例一个轻量级网络 actor。命令只在此处启动网络操作,
566
+ // 不等待 RPC 响应,避免应用回调中的 send 反向占住收包路径。
567
+ _actorTail = Promise.resolve();
568
+ _actorBusy = false;
263
569
  constructor(opts) {
264
570
  this._dispatcher = opts.eventDispatcher;
265
571
  this._timeout = opts.timeout ?? 10;
@@ -287,18 +593,21 @@ export class RPCTransport {
287
593
  setMetaObserver(observer) {
288
594
  this._metaObserver = observer;
289
595
  }
290
- async _notifyMetaObserver(message) {
291
- if (this._metaObserver === null)
596
+ _notifyMetaObserver(message) {
597
+ const observer = this._metaObserver;
598
+ if (observer === null)
292
599
  return;
293
600
  const meta = message._meta;
294
601
  if (!isJsonObject(meta))
295
602
  return;
296
- try {
297
- await this._metaObserver(meta);
298
- }
299
- catch (exc) {
300
- this._log.debug(`meta_observer raised: ${String(exc)}`);
301
- }
603
+ this._dispatcher.enqueueTask(async () => {
604
+ try {
605
+ await observer(meta);
606
+ }
607
+ catch (exc) {
608
+ this._log.debug(`meta_observer raised: ${String(exc)}`);
609
+ }
610
+ });
302
611
  }
303
612
  /** 设置 trace 模式:off / log / diag */
304
613
  setTraceMode(mode) {
@@ -335,7 +644,10 @@ export class RPCTransport {
335
644
  * 连接到 WebSocket URL。
336
645
  * 等待首条消息,若为 challenge 则返回,否则进入消息路由。
337
646
  */
338
- async connect(url) {
647
+ connect(url) {
648
+ return this._enqueueActorStart(() => this._connectImpl(url));
649
+ }
650
+ async _connectImpl(url) {
339
651
  const tStart = Date.now();
340
652
  this._log.debug(`connect enter: url=${url}`);
341
653
  const setup = await this._withConnectionSetup(async () => {
@@ -345,7 +657,7 @@ export class RPCTransport {
345
657
  this._lastCloseCode = null;
346
658
  this._lastCloseReason = '';
347
659
  const handshake = new Promise((resolve, reject) => {
348
- const ws = new WebSocket(url);
660
+ const ws = createTransportWebSocket(url);
349
661
  this._ws = ws;
350
662
  this._closed = false;
351
663
  let initialResolved = false;
@@ -489,7 +801,10 @@ export class RPCTransport {
489
801
  return setup.handshake;
490
802
  }
491
803
  /** 关闭连接 */
492
- async close() {
804
+ close() {
805
+ return this._enqueueActor(() => this._closeImpl());
806
+ }
807
+ async _closeImpl() {
493
808
  await this._withConnectionSetup(() => this._closeUnlocked());
494
809
  }
495
810
  /** 已持有连接建立串行权时关闭当前 WebSocket。 */
@@ -566,7 +881,10 @@ export class RPCTransport {
566
881
  * 发起 JSON-RPC 2.0 调用。
567
882
  * 返回 result 字段的值;若有 error 字段则抛出映射后的错误。
568
883
  */
569
- async call(method, params, timeout, trace, background = false) {
884
+ call(method, params, timeout, trace, background = false) {
885
+ return this._enqueueActorStart(() => this._callImpl(method, params, timeout, trace, background), () => new TimeoutError(`rpc cancelled: ${method}`, { retryable: true }));
886
+ }
887
+ _callImpl(method, params, timeout, trace, background = false) {
570
888
  if (this._closed || !this._ws) {
571
889
  throw this._notConnectedError();
572
890
  }
@@ -612,7 +930,7 @@ export class RPCTransport {
612
930
  this._drainRpcQueue();
613
931
  }, effectiveTimeout);
614
932
  const pending = {
615
- resolve: async (response) => {
933
+ resolve: (response) => {
616
934
  clearTimeout(timer);
617
935
  const elapsed = Date.now() - tStart;
618
936
  if (response.error !== undefined) {
@@ -631,7 +949,7 @@ export class RPCTransport {
631
949
  if (traceId) {
632
950
  this._log.info(`[trace=${traceId}] rpc_recv method=${method} rpc_id=${rpcId} duration_ms=${elapsed} status=ok`);
633
951
  }
634
- await this._notifyMetaObserver(response);
952
+ this._notifyMetaObserver(response);
635
953
  // 处理 success 路径的 _trace
636
954
  const respTrace = response._trace;
637
955
  if (respTrace && typeof respTrace === 'object' && !Array.isArray(respTrace)) {
@@ -671,7 +989,10 @@ export class RPCTransport {
671
989
  return Object.assign(promise, { cancel: () => cancelRpc?.() });
672
990
  }
673
991
  /** 发送 JSON-RPC 2.0 Notification,不分配 id,也不等待响应。 */
674
- async notify(method, params) {
992
+ notify(method, params) {
993
+ return this._enqueueActorStart(() => this._notifyImpl(method, params));
994
+ }
995
+ async _notifyImpl(method, params) {
675
996
  if (this._closed || !this._ws) {
676
997
  throw this._notConnectedError();
677
998
  }
@@ -699,6 +1020,63 @@ export class RPCTransport {
699
1020
  this._sendChain = run.catch(() => { });
700
1021
  return run;
701
1022
  }
1023
+ _enqueueActor(operation) {
1024
+ this._actorBusy = true;
1025
+ const run = this._actorTail.then(async () => {
1026
+ return await operation();
1027
+ }, async () => {
1028
+ return await operation();
1029
+ });
1030
+ const tail = run.then(() => undefined, () => undefined);
1031
+ this._actorTail = tail;
1032
+ void tail.then(() => {
1033
+ if (this._actorTail === tail)
1034
+ this._actorBusy = false;
1035
+ });
1036
+ return run;
1037
+ }
1038
+ _enqueueActorStart(operation, cancellationError) {
1039
+ let resolveResult;
1040
+ let rejectResult;
1041
+ const result = new Promise((resolve, reject) => { resolveResult = resolve; rejectResult = reject; });
1042
+ let cancel;
1043
+ let cancelRequested = false;
1044
+ const launch = () => {
1045
+ if (cancelRequested)
1046
+ return;
1047
+ try {
1048
+ const inner = operation();
1049
+ cancel = inner.cancel;
1050
+ inner.then(resolveResult, rejectResult);
1051
+ }
1052
+ catch (err) {
1053
+ rejectResult(err);
1054
+ }
1055
+ };
1056
+ if (this._actorBusy) {
1057
+ const gate = this._actorTail.then(launch, launch);
1058
+ const tail = gate.then(() => undefined, () => undefined);
1059
+ this._actorTail = tail;
1060
+ void tail.then(() => {
1061
+ if (this._actorTail === tail)
1062
+ this._actorBusy = false;
1063
+ });
1064
+ }
1065
+ else {
1066
+ launch();
1067
+ }
1068
+ return Object.assign(result, {
1069
+ cancel: () => {
1070
+ if (cancelRequested)
1071
+ return;
1072
+ cancelRequested = true;
1073
+ if (cancel)
1074
+ cancel();
1075
+ else
1076
+ rejectResult(cancellationError?.() ?? new TimeoutError('rpc cancelled', { retryable: true }));
1077
+ },
1078
+ });
1079
+ }
702
1080
  _sendText(payload, context, beforeSend) {
703
1081
  return this._enqueueSend(async () => {
704
1082
  if (beforeSend && !beforeSend()) {
@@ -709,7 +1087,7 @@ export class RPCTransport {
709
1087
  }
710
1088
  const ws = this._ws;
711
1089
  try {
712
- ws.send(payload);
1090
+ await Promise.resolve(ws.send(payload));
713
1091
  }
714
1092
  catch (err) {
715
1093
  throw await this._sendFailureError(context, err, ws);
@@ -841,7 +1219,15 @@ export class RPCTransport {
841
1219
  const enriched = { ...respTrace, spans };
842
1220
  this._log.info(traceDisplay(method, status, elapsedMs, respTrace, spans));
843
1221
  if (this._traceObserver !== null) {
844
- this._traceObserver({ type: 'rpc', method, trace: enriched, status, duration_ms: elapsedMs });
1222
+ const observer = this._traceObserver;
1223
+ this._dispatcher.enqueueTask(async () => {
1224
+ try {
1225
+ await observer({ type: 'rpc', method, trace: enriched, status, duration_ms: elapsedMs });
1226
+ }
1227
+ catch (err) {
1228
+ this._log.debug(`trace observer raised: ${err instanceof Error ? err.message : String(err)}`);
1229
+ }
1230
+ });
845
1231
  }
846
1232
  }
847
1233
  catch (err) {
@@ -887,10 +1273,18 @@ export class RPCTransport {
887
1273
  // 非主动关闭时触发断线回调
888
1274
  if (!wasClosed) {
889
1275
  const error = new ConnectionError(`websocket closed: code=${event.code} reason=${event.reason}`);
890
- this._dispatcher.publish('connection.error', { error });
891
1276
  if (this._onDisconnect) {
892
- this._onDisconnect(error, event.code).catch(exc => this._log.warn('[aun_core.transport] disconnect callback exception:', exc));
1277
+ const onDisconnect = this._onDisconnect;
1278
+ this._dispatcher.enqueueTask(async () => {
1279
+ try {
1280
+ await onDisconnect(error, event.code);
1281
+ }
1282
+ catch (exc) {
1283
+ this._log.warn('[aun_core.transport] disconnect callback exception:', exc);
1284
+ }
1285
+ });
893
1286
  }
1287
+ this._dispatcher.enqueue('connection.error', { error });
894
1288
  }
895
1289
  }
896
1290
  _notConnectedError() {
@@ -922,7 +1316,7 @@ export class RPCTransport {
922
1316
  if (method === 'challenge') {
923
1317
  this._challenge = message;
924
1318
  this._log.debug('challenge received');
925
- this._dispatcher.publish('connection.challenge', message.params ?? {});
1319
+ this._dispatcher.enqueue('connection.challenge', message.params ?? {});
926
1320
  return;
927
1321
  }
928
1322
  // 事件推送
@@ -930,7 +1324,7 @@ export class RPCTransport {
930
1324
  const protocolEvent = method.slice(6);
931
1325
  const sdkEvent = EVENT_NAME_MAP[protocolEvent] ?? protocolEvent;
932
1326
  this._log.debug(`event recv: event=${sdkEvent} ${summarizeDict(message.params, DIAG_RESULT_FIELDS)}`);
933
- void this._notifyMetaObserver(message);
1327
+ this._notifyMetaObserver(message);
934
1328
  // 提取事件中的 _trace 并回调 observer,然后从 params 中剥离
935
1329
  const params = (message.params ?? {});
936
1330
  if ('_trace' in params) {
@@ -938,10 +1332,14 @@ export class RPCTransport {
938
1332
  delete params._trace;
939
1333
  if (eventTrace && typeof eventTrace === 'object' && !Array.isArray(eventTrace)) {
940
1334
  if (this._traceObserver !== null) {
941
- try {
942
- this._traceObserver({ type: 'event', event: sdkEvent, trace: eventTrace });
943
- }
944
- catch { /* observer 抛错被吞 */ }
1335
+ const observer = this._traceObserver;
1336
+ const tracePayload = eventTrace;
1337
+ this._dispatcher.enqueueTask(async () => {
1338
+ try {
1339
+ await observer({ type: 'event', event: sdkEvent, trace: tracePayload });
1340
+ }
1341
+ catch { /* observer 抛错被吞 */ }
1342
+ });
945
1343
  }
946
1344
  const traceObj = eventTrace;
947
1345
  this._log.info(`[trace=${String(traceObj.trace_id ?? '')}] event_recv event=${sdkEvent}`);
@@ -949,16 +1347,16 @@ export class RPCTransport {
949
1347
  }
950
1348
  // 发布为 _raw.{event},由 AUNClient 处理后再发布用户可见的事件
951
1349
  if (sdkEvent.startsWith('app.')) {
952
- this._dispatcher.publish(sdkEvent, params);
1350
+ this._dispatcher.enqueue(sdkEvent, params);
953
1351
  return;
954
1352
  }
955
- this._dispatcher.publish(`_raw.${sdkEvent}`, params);
1353
+ this._dispatcher.enqueue(`_raw.${sdkEvent}`, params);
956
1354
  return;
957
1355
  }
958
1356
  // 其他通知
959
- void this._notifyMetaObserver(message);
1357
+ this._notifyMetaObserver(message);
960
1358
  this._log.debug(`notification recv: method=${method || '<no-method>'}`);
961
- this._dispatcher.publish('notification', message);
1359
+ this._dispatcher.enqueue('notification', message);
962
1360
  }
963
1361
  _decodeMessage(raw) {
964
1362
  if (isJsonObject(raw)) {