@agentunion/fastaun 0.5.3 → 0.5.6

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 (133) hide show
  1. package/CHANGELOG.md +126 -52
  2. package/_packed_docs/AUN/347/246/273/347/272/277/346/216/250/351/200/201/346/216/245/345/205/245/344/270/216/346/274/224/347/244/272/346/214/207/345/215/227.md +290 -0
  3. package/_packed_docs/AUN/347/246/273/347/272/277/346/216/250/351/200/201/346/234/215/345/212/241/346/236/266/346/236/204/344/270/216/350/257/246/347/273/206/345/256/236/347/216/260/350/256/241/345/210/222-codex.md +994 -0
  4. package/_packed_docs/AUN/347/246/273/347/272/277/346/216/250/351/200/201/346/234/215/345/212/241/350/277/220/347/273/264/344/270/216/345/217/221/345/270/203/346/214/207/345/215/227.md +144 -0
  5. package/_packed_docs/CHANGELOG.md +126 -52
  6. package/_packed_docs/INDEX.md +127 -69
  7. package/_packed_docs/KITE_DOCS_GUIDE.md +63 -29
  8. package/_packed_docs/audit/AUN/346/234/215/345/212/241Go/345/214/226/351/207/215/346/236/204/347/262/276/347/273/206/345/214/226/346/272/220/347/240/201/345/256/241/346/237/245-20260718.md +366 -0
  9. package/_packed_docs/aun/345/205/254/347/275/221/346/265/213/350/257/225/350/277/220/350/241/214/346/214/207/345/215/227.md +227 -0
  10. package/_packed_docs/aun/345/210/206/345/270/203/345/274/217/346/265/213/350/257/225/350/277/220/350/241/214/346/214/207/345/215/227.md +1004 -0
  11. package/_packed_docs/aun/346/265/213/350/257/225/350/277/220/350/241/214/346/214/207/345/215/227.md +999 -0
  12. package/_packed_docs/cli/CLI/346/211/213/345/206/214.md +6 -4
  13. package/_packed_docs/protocol/06-/346/234/215/345/212/241/345/215/217/350/256/256.md +58 -20
  14. package/_packed_docs/protocol/10-Group-/345/255/220/345/215/217/350/256/256.md +219 -247
  15. package/_packed_docs/protocol/12-Stream-/345/255/220/345/215/217/350/256/256.md +14 -14
  16. package/_packed_docs/protocol/13-Agent/350/241/214/344/270/272/350/247/204/350/214/203.md +3 -3
  17. package/_packed_docs/protocol/15-/347/246/273/347/272/277/346/216/250/351/200/201/351/200/232/347/237/245/345/215/217/350/256/256.md +165 -421
  18. package/_packed_docs/protocol/README.md +1 -0
  19. package/_packed_docs/protocol/aun-docs-guide.md +7 -4
  20. package/_packed_docs/protocol/index.md +25 -19
  21. package/_packed_docs/sdk/02-WebSocket/345/215/217/350/256/256.md +62 -17
  22. package/_packed_docs/sdk/03-/346/240/270/345/277/203/346/246/202/345/277/265.md +22 -0
  23. package/_packed_docs/sdk/04-/350/277/236/346/216/245/344/270/216/350/256/244/350/257/201.md +67 -78
  24. package/_packed_docs/sdk/05-E2EE/345/212/240/345/257/206/351/200/232/344/277/241.md +6 -2
  25. package/_packed_docs/sdk/06-API/346/211/213/345/206/214.md +87 -41
  26. package/_packed_docs/sdk/08-/346/234/200/344/275/263/345/256/236/350/267/265.md +18 -5
  27. package/_packed_docs/sdk/09-group-rpc-manual.md +307 -267
  28. package/_packed_docs/sdk/09-message-rpc-manual.md +142 -103
  29. package/_packed_docs/sdk/09-payload-reference.md +1 -1
  30. package/_packed_docs/sdk/09-stream-rpc-manual.md +8 -8
  31. package/_packed_docs/sdk/AUN_DOCS_GUIDE.md +26 -17
  32. package/_packed_docs/sdk/INDEX.md +42 -34
  33. package/_packed_docs/sdk/README.md +7 -6
  34. package/_packed_docs//346/266/210/346/201/257/345/220/214/346/255/245/344/270/216/345/216/206/345/217/262/346/213/211/345/217/226/344/273/243/347/240/201/345/256/241/346/237/245/351/227/256/351/242/230/346/270/205/345/215/225.md +229 -0
  35. package/_packed_docs//346/266/210/346/201/257/345/220/214/346/255/245/344/270/216/345/216/206/345/217/262/346/213/211/345/217/226/346/224/271/351/200/240/346/226/271/346/241/210.md +750 -0
  36. package/dist/agent-md.js +8 -2
  37. package/dist/agent-md.js.map +1 -1
  38. package/dist/aid-store.d.ts +2 -0
  39. package/dist/aid-store.js +43 -7
  40. package/dist/aid-store.js.map +1 -1
  41. package/dist/auth.d.ts +0 -2
  42. package/dist/auth.js +97 -50
  43. package/dist/auth.js.map +1 -1
  44. package/dist/client/delivery.d.ts +70 -7
  45. package/dist/client/delivery.js +1334 -236
  46. package/dist/client/delivery.js.map +1 -1
  47. package/dist/client/group-state.js +25 -22
  48. package/dist/client/group-state.js.map +1 -1
  49. package/dist/client/lifecycle.js +46 -3
  50. package/dist/client/lifecycle.js.map +1 -1
  51. package/dist/client/mention-mode.d.ts +6 -0
  52. package/dist/client/mention-mode.js +184 -0
  53. package/dist/client/mention-mode.js.map +1 -0
  54. package/dist/client/peers.d.ts +1 -1
  55. package/dist/client/peers.js +26 -3
  56. package/dist/client/peers.js.map +1 -1
  57. package/dist/client/rpc-pipeline.d.ts +20 -1
  58. package/dist/client/rpc-pipeline.js +332 -73
  59. package/dist/client/rpc-pipeline.js.map +1 -1
  60. package/dist/client/v2-e2ee.d.ts +20 -1
  61. package/dist/client/v2-e2ee.js +625 -105
  62. package/dist/client/v2-e2ee.js.map +1 -1
  63. package/dist/client.d.ts +17 -5
  64. package/dist/client.js +332 -167
  65. package/dist/client.js.map +1 -1
  66. package/dist/errors.js +4 -1
  67. package/dist/errors.js.map +1 -1
  68. package/dist/facades.d.ts +6 -0
  69. package/dist/facades.js +196 -80
  70. package/dist/facades.js.map +1 -1
  71. package/dist/group-index.d.ts +5 -0
  72. package/dist/group-index.js +65 -30
  73. package/dist/group-index.js.map +1 -1
  74. package/dist/keystore/aid-db.d.ts +12 -0
  75. package/dist/keystore/aid-db.js +68 -4
  76. package/dist/keystore/aid-db.js.map +1 -1
  77. package/dist/keystore/index.d.ts +12 -0
  78. package/dist/keystore/local-identity-store.d.ts +10 -0
  79. package/dist/keystore/local-identity-store.js +8 -2
  80. package/dist/keystore/local-identity-store.js.map +1 -1
  81. package/dist/keystore/local-token-store.d.ts +10 -0
  82. package/dist/keystore/local-token-store.js +8 -2
  83. package/dist/keystore/local-token-store.js.map +1 -1
  84. package/dist/register-flow.d.ts +0 -1
  85. package/dist/register-flow.js +96 -38
  86. package/dist/register-flow.js.map +1 -1
  87. package/dist/secret-store/file-store.d.ts +3 -14
  88. package/dist/secret-store/file-store.js +7 -98
  89. package/dist/secret-store/file-store.js.map +1 -1
  90. package/dist/secret-store/index.d.ts +1 -4
  91. package/dist/secret-store/index.js +2 -2
  92. package/dist/secret-store/index.js.map +1 -1
  93. package/dist/seq-tracker.d.ts +29 -9
  94. package/dist/seq-tracker.js +233 -64
  95. package/dist/seq-tracker.js.map +1 -1
  96. package/dist/storage/vfs.d.ts +1 -0
  97. package/dist/storage/vfs.js +28 -4
  98. package/dist/storage/vfs.js.map +1 -1
  99. package/dist/tools/cross-sdk-agent.js +360 -8
  100. package/dist/tools/cross-sdk-agent.js.map +1 -1
  101. package/dist/transport.d.ts +19 -0
  102. package/dist/transport.js +197 -14
  103. package/dist/transport.js.map +1 -1
  104. package/dist/v2/e2ee/decrypt.js +26 -23
  105. package/dist/v2/e2ee/decrypt.js.map +1 -1
  106. package/dist/v2/session/session.js +3 -4
  107. package/dist/v2/session/session.js.map +1 -1
  108. package/dist/v2/state/commitment.js +1 -2
  109. package/dist/v2/state/commitment.js.map +1 -1
  110. package/dist/version.d.ts +1 -1
  111. package/dist/version.js +1 -1
  112. package/package.json +5 -4
  113. package/dist/e2ee-group.d.ts +0 -249
  114. package/dist/e2ee-group.js +0 -1567
  115. package/dist/e2ee-group.js.map +0 -1
  116. package/dist/group-resources.d.ts +0 -31
  117. package/dist/group-resources.js +0 -84
  118. package/dist/group-resources.js.map +0 -1
  119. package/dist/keystore/file.d.ts +0 -109
  120. package/dist/keystore/file.js +0 -772
  121. package/dist/keystore/file.js.map +0 -1
  122. package/dist/keystore/sqlite-backup.d.ts +0 -44
  123. package/dist/keystore/sqlite-backup.js +0 -382
  124. package/dist/keystore/sqlite-backup.js.map +0 -1
  125. package/dist/namespaces/auth.d.ts +0 -118
  126. package/dist/namespaces/auth.js +0 -968
  127. package/dist/namespaces/auth.js.map +0 -1
  128. package/dist/namespaces/custody.d.ts +0 -48
  129. package/dist/namespaces/custody.js +0 -313
  130. package/dist/namespaces/custody.js.map +0 -1
  131. package/dist/namespaces/meta.d.ts +0 -76
  132. package/dist/namespaces/meta.js +0 -490
  133. package/dist/namespaces/meta.js.map +0 -1
@@ -7,6 +7,7 @@ import { URL } from 'node:url';
7
7
  import { AUNClient } from '../client.js';
8
8
  import { AIDStore } from '../aid-store.js';
9
9
  import { publicKeyFingerprint } from '../cert-utils.js';
10
+ import { VERSION } from '../version.js';
10
11
  function envBool(name, fallback = false) {
11
12
  const raw = process.env[name];
12
13
  if (raw == null)
@@ -106,7 +107,7 @@ function sendJson(res, status, data) {
106
107
  }
107
108
  class CrossSdkTsAgent {
108
109
  language = 'ts';
109
- sdkVersion = 'unknown';
110
+ sdkVersion = VERSION;
110
111
  aid = textOf(process.env.AUN_TEST_AID || 'cross-ts.agentid.pub').trim();
111
112
  issuer = textOf(process.env.AUN_TEST_ISSUER || 'agentid.pub').trim() || 'agentid.pub';
112
113
  gatewayAid = textOf(process.env.AUN_GATEWAY_AID || `gateway.${this.issuer}`).trim();
@@ -118,16 +119,38 @@ class CrossSdkTsAgent {
118
119
  startupError = '';
119
120
  inbox = [];
120
121
  groupInbox = [];
122
+ rawPushObservations = [];
123
+ rawPushObservationId = '';
124
+ rawPushRpcMethods = [];
125
+ rpcObservedTransports = new WeakSet();
126
+ groupChangedEvents = [];
121
127
  traces = new Map();
122
128
  sendResults = new Map();
129
+ inlineRealtimePayloadV1 = true;
123
130
  constructor() {
124
- this.client = new AUNClient();
125
- const internal = this.client;
131
+ this.client = this.newClient();
132
+ }
133
+ async start() {
134
+ this.bindClientEvents();
135
+ // Cross-SDK runner 需要先由显式 Tail 建立窗口证明;启动阶段不得遗留后台 Pull。
136
+ await this.ensureConnected(false);
137
+ this.ready = true;
138
+ }
139
+ newClient() {
140
+ const client = new AUNClient();
141
+ const internal = client;
126
142
  if (internal._configModel)
127
143
  internal._configModel.requireForwardSecrecy = false;
128
144
  internal._testSlotId = this.slotId;
145
+ return client;
129
146
  }
130
- async start() {
147
+ bindClientEvents() {
148
+ this.installTransportRpcObserver();
149
+ this.client.on('_raw.message.received', (data) => this.storeRawPushObservation('_raw.message.received', 'p2p', 'legacy', data));
150
+ this.client.on('_raw.peer.v2.message_received', (data) => this.storeRawPushObservation('_raw.peer.v2.message_received', 'p2p', 'v2', data));
151
+ this.client.on('_raw.group.message_created', (data) => this.storeRawPushObservation('_raw.group.message_created', 'group', 'legacy', data));
152
+ this.client.on('_raw.group.v2.message_created', (data) => this.storeRawPushObservation('_raw.group.v2.message_created', 'group', 'v2', data));
153
+ this.client.on('_raw.group.changed', (data) => this.storeRawGroupChangedObservation(data));
131
154
  this.client.on('message.received', (msg) => {
132
155
  void this.storeInboxItem(this.normalizeMessage(msg, true));
133
156
  });
@@ -140,8 +163,103 @@ class CrossSdkTsAgent {
140
163
  this.client.on('group.message_undecryptable', (msg) => {
141
164
  void this.storeGroupInboxItem(this.normalizeGroupMessage(msg, false, 'undecryptable'));
142
165
  });
143
- await this.ensureConnected();
144
- this.ready = true;
166
+ this.client.on('group.changed', (data) => this.storeGroupChangedPublicEvent(data));
167
+ }
168
+ installTransportRpcObserver() {
169
+ const transport = this.client._transport;
170
+ transport?.setTraceMode('diag');
171
+ if (!transport?.call || this.rpcObservedTransports.has(transport))
172
+ return;
173
+ const originalCall = transport.call;
174
+ this.rpcObservedTransports.add(transport);
175
+ transport.call = async (method, params, timeout, trace, background) => {
176
+ this.storeRawPushRpcMethod({ type: 'rpc', method, request_window: this.rawPushRequestWindow(params) });
177
+ let requestParams = params;
178
+ if (method === 'auth.connect' && !this.inlineRealtimePayloadV1 && jsonObjectOf(params)) {
179
+ const request = { ...jsonObjectOf(params) };
180
+ const capabilities = jsonObjectOf(request.capabilities);
181
+ if (capabilities) {
182
+ delete capabilities.inline_realtime_payload_v1;
183
+ request.capabilities = capabilities;
184
+ }
185
+ requestParams = request;
186
+ }
187
+ return await originalCall.call(transport, method, requestParams, timeout, trace, background);
188
+ };
189
+ }
190
+ storeRawPushObservation(rawEvent, scope, defaultKind, value) {
191
+ const data = jsonObjectOf(value) ?? {};
192
+ const hasInline = Object.prototype.hasOwnProperty.call(data, 'inline_message');
193
+ const inlineMessage = jsonObjectOf(data.inline_message);
194
+ this.rawPushObservations.push({
195
+ observation_id: this.rawPushObservationId,
196
+ time: Date.now(), raw_event: rawEvent, scope, group_id: textOf(data.group_id),
197
+ kind: textOf(data.kind || defaultKind),
198
+ action: textOf(data.action),
199
+ event_seq: Number(data.event_seq ?? 0) || 0,
200
+ seq: Number(data.seq ?? data.message_seq ?? data.msg_seq ?? 0) || 0,
201
+ message_id: textOf(data.message_id ?? data.id ?? ''),
202
+ has_inline: hasInline,
203
+ inline_version: textOf(data.inline_version ?? inlineMessage?.version ?? ''),
204
+ inline_is_object: inlineMessage !== null,
205
+ target_device_present: Boolean(textOf(data.target_device_id)),
206
+ target_slot_present: Boolean(textOf(data.target_slot_id)),
207
+ target_device_id: textOf(data.target_device_id),
208
+ target_slot_id: textOf(data.target_slot_id),
209
+ inline_candidate_keys: Object.keys(data).filter((key) => key.startsWith('inline_') || key.startsWith('_inline_message_candidates_') || key === '_group_inline_plain_source_v1' || key === '_group_inline_v2_source_v1').sort(),
210
+ top_level_sensitive_keys: Object.keys(data).filter((key) => [
211
+ 'inline_message', 'payload', 'message', 'envelope_json', 'ciphertext',
212
+ '_inline_message_candidates_v1', '_group_inline_plain_source_v1', '_group_inline_v2_source_v1',
213
+ ].includes(key)).sort(),
214
+ });
215
+ if (this.rawPushObservations.length > 1000)
216
+ this.rawPushObservations = this.rawPushObservations.slice(-1000);
217
+ }
218
+ storeRawGroupChangedObservation(value) {
219
+ this.storeRawPushObservation('_raw.group.changed', 'group', 'changed', value);
220
+ }
221
+ storeGroupChangedPublicEvent(value) {
222
+ const data = jsonObjectOf(value) ?? {};
223
+ this.groupChangedEvents.push({
224
+ observation_id: this.rawPushObservationId,
225
+ time: Date.now(),
226
+ group_id: textOf(data.group_id),
227
+ action: textOf(data.action),
228
+ event_seq: Number(data.event_seq ?? 0) || 0,
229
+ event_id: textOf(data.event_id),
230
+ envelope_present: jsonObjectOf(data.envelope) !== null,
231
+ });
232
+ if (this.groupChangedEvents.length > 1000)
233
+ this.groupChangedEvents = this.groupChangedEvents.slice(-1000);
234
+ }
235
+ storeRawPushRpcMethod(traceInfo) {
236
+ if (traceInfo.type !== 'rpc')
237
+ return;
238
+ if (!this.rawPushObservationId)
239
+ return;
240
+ const method = textOf(traceInfo.method).trim();
241
+ if (!method)
242
+ return;
243
+ const requestWindow = jsonObjectOf(traceInfo.request_window) ?? {};
244
+ this.rawPushRpcMethods.push({
245
+ observation_id: this.rawPushObservationId,
246
+ time: Date.now(),
247
+ method,
248
+ ...this.rawPushRequestWindow(requestWindow),
249
+ });
250
+ if (this.rawPushRpcMethods.length > 1000)
251
+ this.rawPushRpcMethods = this.rawPushRpcMethods.slice(-1000);
252
+ }
253
+ rawPushRequestWindow(value) {
254
+ const params = jsonObjectOf(value) ?? {};
255
+ const afterSeq = params.after_seq;
256
+ const limit = params.limit;
257
+ // 仅返回恢复窗口元数据;不得将 RPC params(其中可能含正文)暴露给测试接口。
258
+ return {
259
+ window_mode: typeof params.window_mode === 'string' ? params.window_mode : '',
260
+ after_seq: typeof afterSeq === 'number' && Number.isInteger(afterSeq) ? afterSeq : null,
261
+ limit: typeof limit === 'number' && Number.isInteger(limit) ? limit : null,
262
+ };
145
263
  }
146
264
  async close() {
147
265
  try {
@@ -151,7 +269,7 @@ class CrossSdkTsAgent {
151
269
  // 退出清理失败不影响容器关闭。
152
270
  }
153
271
  }
154
- async ensureConnected() {
272
+ async ensureConnected(backgroundSync = true) {
155
273
  const internal = this.client;
156
274
  const store = new AIDStore({
157
275
  aunPath: this.aunPath,
@@ -179,7 +297,8 @@ class CrossSdkTsAgent {
179
297
  throw new Error(`load identity failed: ${loaded.error.code}: ${loaded.error.message}`);
180
298
  }
181
299
  this.client.loadIdentity(loaded.data.aid);
182
- await this.client.connect();
300
+ this.installTransportRpcObserver();
301
+ await this.client.connect({ auto_reconnect: false, background_sync: backgroundSync });
183
302
  }
184
303
  identity() {
185
304
  const internal = this.client;
@@ -292,6 +411,46 @@ class CrossSdkTsAgent {
292
411
  sendJson(res, 200, { ok: true });
293
412
  return;
294
413
  }
414
+ if (req.method === 'POST' && url.pathname === '/test/raw-push/reset') {
415
+ const body = await readJson(req);
416
+ this.rawPushObservations = [];
417
+ this.rawPushRpcMethods = [];
418
+ this.groupChangedEvents = [];
419
+ this.rawPushObservationId = textOf(body.observation_id ?? '');
420
+ sendJson(res, 200, { ok: true, observation_id: this.rawPushObservationId });
421
+ return;
422
+ }
423
+ if (req.method === 'GET' && url.pathname === '/test/raw-push') {
424
+ const filters = {};
425
+ for (const key of ['observation_id', 'scope', 'group_id', 'kind', 'raw_event'])
426
+ filters[key] = textOf(url.searchParams.get(key) ?? '');
427
+ const limit = Math.max(1, Math.min(Number(url.searchParams.get('limit') ?? 1000) || 1000, 1000));
428
+ let items = [...this.rawPushObservations];
429
+ for (const [key, value] of Object.entries(filters))
430
+ if (value)
431
+ items = items.filter((item) => textOf(item[key]) === value);
432
+ items = items.slice(-limit);
433
+ const observationId = filters.observation_id;
434
+ let rpcEntries = [...this.rawPushRpcMethods];
435
+ let groupChangedEvents = [...this.groupChangedEvents];
436
+ if (observationId)
437
+ rpcEntries = rpcEntries.filter((item) => textOf(item.observation_id) === observationId);
438
+ if (observationId)
439
+ groupChangedEvents = groupChangedEvents.filter((item) => textOf(item.observation_id) === observationId);
440
+ if (filters.group_id)
441
+ groupChangedEvents = groupChangedEvents.filter((item) => textOf(item.group_id) === filters.group_id);
442
+ rpcEntries = rpcEntries.slice(-limit);
443
+ const rpcMethods = rpcEntries.map((item) => textOf(item.method));
444
+ const rpcMethodCounts = {};
445
+ for (const method of rpcMethods)
446
+ rpcMethodCounts[method] = Number(rpcMethodCounts[method] ?? 0) + 1;
447
+ sendJson(res, 200, {
448
+ ok: true, items, count: items.length, rpc_methods: rpcMethods, rpc_method_counts: rpcMethodCounts,
449
+ rpc_calls: rpcEntries,
450
+ group_changed_events: groupChangedEvents.slice(-limit),
451
+ });
452
+ return;
453
+ }
295
454
  if (req.method === 'GET' && url.pathname === '/identity') {
296
455
  sendJson(res, 200, this.identity());
297
456
  return;
@@ -308,6 +467,34 @@ class CrossSdkTsAgent {
308
467
  await this.pull(req, res);
309
468
  return;
310
469
  }
470
+ if (req.method === 'GET' && url.pathname === '/sync/state') {
471
+ this.syncState(url, res);
472
+ return;
473
+ }
474
+ if (req.method === 'POST' && url.pathname === '/sync/tail') {
475
+ await this.syncTail(req, res);
476
+ return;
477
+ }
478
+ if (req.method === 'POST' && url.pathname === '/sync/forward') {
479
+ await this.syncForward(req, res);
480
+ return;
481
+ }
482
+ if (req.method === 'POST' && url.pathname === '/sync/history') {
483
+ await this.syncHistory(req, res);
484
+ return;
485
+ }
486
+ if (req.method === 'POST' && url.pathname === '/session/disconnect') {
487
+ await this.sessionDisconnect(res);
488
+ return;
489
+ }
490
+ if (req.method === 'POST' && url.pathname === '/session/connect') {
491
+ await this.sessionConnect(req, res);
492
+ return;
493
+ }
494
+ if (req.method === 'POST' && url.pathname === '/session/restart') {
495
+ await this.sessionRestart(req, res);
496
+ return;
497
+ }
311
498
  if (req.method === 'POST' && url.pathname === '/group/create') {
312
499
  await this.groupCreate(req, res);
313
500
  return;
@@ -494,6 +681,171 @@ class CrossSdkTsAgent {
494
681
  });
495
682
  }
496
683
  }
684
+ syncNamespace(scope, groupId = '') {
685
+ if (scope === 'p2p')
686
+ return `p2p:${this.aid}`;
687
+ if (scope === 'group' && groupId)
688
+ return `group:${groupId}`;
689
+ throw new Error('scope must be p2p, or group with group_id');
690
+ }
691
+ syncStateValue(scope, groupId = '') {
692
+ const namespace = this.syncNamespace(scope, groupId);
693
+ const tracker = this.client._seqTracker;
694
+ if (!tracker)
695
+ throw new Error('SeqTracker is unavailable');
696
+ return { namespace, state: tracker.getSyncState(namespace) };
697
+ }
698
+ syncState(url, res) {
699
+ const scope = textOf(url.searchParams.get('scope') || 'p2p').trim().toLowerCase();
700
+ const groupId = textOf(url.searchParams.get('group_id') || '').trim();
701
+ try {
702
+ const snapshot = this.syncStateValue(scope, groupId);
703
+ sendJson(res, 200, { ok: true, scope, ...snapshot });
704
+ }
705
+ catch (err) {
706
+ sendJson(res, 400, {
707
+ ok: false,
708
+ error_code: err instanceof Error ? err.name : 'Error',
709
+ error_message: err instanceof Error ? err.message : String(err),
710
+ });
711
+ }
712
+ }
713
+ async syncTail(req, res) {
714
+ await this.syncPull(req, res, true);
715
+ }
716
+ async syncForward(req, res) {
717
+ await this.syncPull(req, res, false);
718
+ }
719
+ async syncPull(req, res, tail) {
720
+ const body = await readJson(req);
721
+ const scope = textOf(body.scope || 'p2p').trim().toLowerCase();
722
+ const groupId = textOf(body.group_id || '').trim();
723
+ const afterSeq = Number(body.after_seq ?? 0) || 0;
724
+ const limit = Number(body.limit ?? 50) || 50;
725
+ try {
726
+ const params = { after_seq: afterSeq, limit };
727
+ if (tail)
728
+ params.window_mode = 'tail';
729
+ const method = scope === 'group' ? 'group.pull' : 'message.pull';
730
+ if (scope === 'group')
731
+ params.group_id = groupId;
732
+ this.syncNamespace(scope, groupId);
733
+ let result;
734
+ if (tail) {
735
+ result = await this.client.call(method, params);
736
+ }
737
+ else {
738
+ const transport = this.client._transport;
739
+ if (!transport?.call)
740
+ throw new Error('raw transport is unavailable');
741
+ const wireMethod = scope === 'group' ? 'group.v2.pull' : 'message.v2.pull';
742
+ result = await transport.call(wireMethod, params);
743
+ }
744
+ const resultObj = jsonObjectOf(result) ?? {};
745
+ const messages = Array.isArray(resultObj.messages) ? resultObj.messages : [];
746
+ if (tail) {
747
+ for (const msg of messages) {
748
+ if (scope === 'group')
749
+ await this.storeGroupInboxItem(this.normalizeGroupMessage(msg, true));
750
+ else
751
+ await this.storeInboxItem(this.normalizeMessage(msg, true));
752
+ }
753
+ }
754
+ const snapshot = this.syncStateValue(scope, groupId);
755
+ sendJson(res, 200, { ok: true, scope, ...snapshot, result: jsonSafe(result) });
756
+ }
757
+ catch (err) {
758
+ sendJson(res, 500, {
759
+ ok: false,
760
+ scope,
761
+ error_code: err instanceof Error ? err.name : 'Error',
762
+ error_message: err instanceof Error ? err.message : String(err),
763
+ });
764
+ }
765
+ }
766
+ async syncHistory(req, res) {
767
+ const body = await readJson(req);
768
+ const scope = textOf(body.scope || 'p2p').trim().toLowerCase();
769
+ const groupId = textOf(body.group_id || '').trim();
770
+ const beforeSeq = Number(body.before_seq ?? 0) || 0;
771
+ const limit = Number(body.limit ?? 50) || 50;
772
+ try {
773
+ const params = { before_seq: beforeSeq, limit };
774
+ const method = scope === 'group' ? 'group.history' : 'message.history';
775
+ if (scope === 'group')
776
+ params.group_id = groupId;
777
+ this.syncNamespace(scope, groupId);
778
+ const result = await this.client.call(method, params);
779
+ const snapshot = this.syncStateValue(scope, groupId);
780
+ sendJson(res, 200, { ok: true, scope, ...snapshot, result: jsonSafe(result) });
781
+ }
782
+ catch (err) {
783
+ sendJson(res, 500, {
784
+ ok: false,
785
+ scope,
786
+ error_code: err instanceof Error ? err.name : 'Error',
787
+ error_message: err instanceof Error ? err.message : String(err),
788
+ });
789
+ }
790
+ }
791
+ async sessionDisconnect(res) {
792
+ try {
793
+ this.ready = false;
794
+ await this.client.disconnect();
795
+ sendJson(res, 200, { ok: true, state: textOf(this.client.state) });
796
+ }
797
+ catch (err) {
798
+ sendJson(res, 500, {
799
+ ok: false,
800
+ error_code: err instanceof Error ? err.name : 'Error',
801
+ error_message: err instanceof Error ? err.message : String(err),
802
+ });
803
+ }
804
+ }
805
+ async sessionConnect(req, res) {
806
+ const body = await readJson(req);
807
+ const backgroundSync = body.background_sync === true;
808
+ if (Object.prototype.hasOwnProperty.call(body, 'inline_realtime_payload_v1')) {
809
+ this.inlineRealtimePayloadV1 = body.inline_realtime_payload_v1 !== false;
810
+ }
811
+ try {
812
+ await this.client.connect({ auto_reconnect: false, background_sync: backgroundSync });
813
+ this.ready = true;
814
+ sendJson(res, 200, { ok: true, background_sync: backgroundSync, inline_realtime_payload_v1: this.inlineRealtimePayloadV1 });
815
+ }
816
+ catch (err) {
817
+ sendJson(res, 500, {
818
+ ok: false,
819
+ error_code: err instanceof Error ? err.name : 'Error',
820
+ error_message: err instanceof Error ? err.message : String(err),
821
+ });
822
+ }
823
+ }
824
+ async sessionRestart(req, res) {
825
+ const body = await readJson(req);
826
+ const backgroundSync = body.background_sync === true;
827
+ if (Object.prototype.hasOwnProperty.call(body, 'inline_realtime_payload_v1')) {
828
+ this.inlineRealtimePayloadV1 = body.inline_realtime_payload_v1 !== false;
829
+ }
830
+ this.ready = false;
831
+ try {
832
+ await this.client.close();
833
+ this.client = this.newClient();
834
+ this.bindClientEvents();
835
+ await this.ensureConnected(backgroundSync);
836
+ this.startupError = '';
837
+ this.ready = true;
838
+ sendJson(res, 200, { ok: true, background_sync: backgroundSync, inline_realtime_payload_v1: this.inlineRealtimePayloadV1 });
839
+ }
840
+ catch (err) {
841
+ this.startupError = `${err instanceof Error ? err.name : 'Error'}: ${err instanceof Error ? err.message : String(err)}`;
842
+ sendJson(res, 500, {
843
+ ok: false,
844
+ error_code: err instanceof Error ? err.name : 'Error',
845
+ error_message: err instanceof Error ? err.message : String(err),
846
+ });
847
+ }
848
+ }
497
849
  async groupCreate(req, res) {
498
850
  const body = await readJson(req);
499
851
  const traceId = textOf(body.trace_id || crypto.randomUUID().replace(/-/g, ''));