@xfxstudio/claworld 0.2.22 → 0.2.23-beta.0

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.
@@ -8,7 +8,7 @@
8
8
  ],
9
9
  "name": "Claworld Persona Relay",
10
10
  "description": "Claworld relay world channel plugin for OpenClaw.",
11
- "version": "0.2.22",
11
+ "version": "0.2.23-beta.0",
12
12
  "configSchema": {
13
13
  "type": "object",
14
14
  "additionalProperties": false,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xfxstudio/claworld",
3
- "version": "0.2.22",
3
+ "version": "0.2.23-beta.0",
4
4
  "description": "Claworld channel plugin for OpenClaw",
5
5
  "type": "module",
6
6
  "main": "index.js",
@@ -1328,6 +1328,30 @@ function createDeliveryReplyDispatcher({
1328
1328
  runtimeOutputSummary.counts[kind] += 1;
1329
1329
  };
1330
1330
 
1331
+ const submitRelayReply = async (replyText) => {
1332
+ if (typeof relayClient?.submitDeliveryReply !== 'function') {
1333
+ throw new Error('relay client does not support reply submission');
1334
+ }
1335
+ return await relayClient.submitDeliveryReply({
1336
+ deliveryId,
1337
+ sessionKey,
1338
+ replyText,
1339
+ source: 'openclaw-autochain',
1340
+ });
1341
+ };
1342
+
1343
+ const submitRelayKeptSilent = async (reason) => {
1344
+ if (typeof relayClient?.submitDeliveryKeptSilent !== 'function') {
1345
+ throw new Error('relay client does not support kept_silent submission');
1346
+ }
1347
+ return await relayClient.submitDeliveryKeptSilent({
1348
+ deliveryId,
1349
+ sessionKey,
1350
+ reason,
1351
+ source: 'openclaw-autochain',
1352
+ });
1353
+ };
1354
+
1331
1355
  const flushReply = async (text) => {
1332
1356
  const normalized = String(text || '').trim();
1333
1357
  if (!normalized || replied || suppressed) return false;
@@ -1335,25 +1359,9 @@ function createDeliveryReplyDispatcher({
1335
1359
  suppressed = true;
1336
1360
  return false;
1337
1361
  }
1338
- if (typeof relayClient.sendReplyAndWaitForAck === 'function') {
1339
- const replyResult = await relayClient.sendReplyAndWaitForAck({
1340
- deliveryId,
1341
- sessionKey,
1342
- replyText: normalized,
1343
- source: 'openclaw-autochain',
1344
- });
1345
- replyTransport = replyResult?.transport || 'websocket';
1346
- replyFallbackUsed = replyResult?.fallbackUsed === true;
1347
- } else {
1348
- relayClient.sendReply({
1349
- deliveryId,
1350
- sessionKey,
1351
- replyText: normalized,
1352
- source: 'openclaw-autochain',
1353
- });
1354
- replyTransport = 'websocket-fire-and-forget';
1355
- replyFallbackUsed = false;
1356
- }
1362
+ const replyResult = await submitRelayReply(normalized);
1363
+ replyTransport = replyResult?.transport || null;
1364
+ replyFallbackUsed = replyResult?.fallbackUsed === true;
1357
1365
  replied = true;
1358
1366
  return true;
1359
1367
  };
@@ -1364,19 +1372,11 @@ function createDeliveryReplyDispatcher({
1364
1372
  suppressed = true;
1365
1373
  return false;
1366
1374
  }
1367
- if (typeof relayClient.sendKeepSilentAndWaitForAck === 'function') {
1368
- const silentResult = await relayClient.sendKeepSilentAndWaitForAck({
1369
- deliveryId,
1370
- sessionKey,
1371
- reason: normalizePluginOptionalText(reason) || 'no_renderable_reply',
1372
- source: 'openclaw-autochain',
1373
- });
1374
- keptSilentTransport = silentResult?.transport || 'websocket';
1375
- keptSilentFallbackUsed = silentResult?.fallbackUsed === true;
1376
- } else {
1377
- keptSilentTransport = 'unsupported';
1378
- keptSilentFallbackUsed = false;
1379
- }
1375
+ const silentResult = await submitRelayKeptSilent(
1376
+ normalizePluginOptionalText(reason) || 'no_renderable_reply',
1377
+ );
1378
+ keptSilentTransport = silentResult?.transport || null;
1379
+ keptSilentFallbackUsed = silentResult?.fallbackUsed === true;
1380
1380
  keptSilent = true;
1381
1381
  return true;
1382
1382
  };
@@ -1709,12 +1709,13 @@ async function maybeBridgeRuntimeDelivery({
1709
1709
  });
1710
1710
 
1711
1711
  try {
1712
- if (typeof relayClient?.sendAcceptedAndWaitForAck === 'function') {
1713
- await relayClient.sendAcceptedAndWaitForAck({
1714
- deliveryId,
1715
- sessionKey,
1716
- source: 'runtime_dispatch',
1717
- });
1712
+ const acceptedResult = await relayClient.acceptDeliveryHttp({
1713
+ deliveryId,
1714
+ sessionKey,
1715
+ source: 'runtime_dispatch',
1716
+ });
1717
+ if (acceptedResult.status < 200 || acceptedResult.status >= 300) {
1718
+ throw new Error(`failed to submit relay delivery acceptance: ${acceptedResult.status}`);
1718
1719
  }
1719
1720
  } catch (error) {
1720
1721
  logger.warn?.(`[claworld:${runtimeAccountId}] delivery acceptance acknowledgement failed`, {
@@ -29,6 +29,21 @@ import {
29
29
  TERMINAL_CLOSE_REASONS,
30
30
  } from './relay-client-shared.js';
31
31
 
32
+ const DELIVERY_VISIBILITY_RETRY_ATTEMPTS = 20;
33
+ const DELIVERY_VISIBILITY_RETRY_DELAY_MS = 10;
34
+
35
+ function isDeliveryVisibilityMiss(result = {}) {
36
+ return Number(result?.status) === 404
37
+ && normalizeOptionalText(result?.body?.error) === 'delivery_not_found';
38
+ }
39
+
40
+ async function waitForDeliveryVisibilityRetry() {
41
+ await new Promise((resolve) => {
42
+ const timer = setTimeout(resolve, DELIVERY_VISIBILITY_RETRY_DELAY_MS);
43
+ if (typeof timer?.unref === 'function') timer.unref();
44
+ });
45
+ }
46
+
32
47
  export class ClaworldRelayClient extends EventEmitter {
33
48
  constructor({
34
49
  logger = console,
@@ -244,6 +259,18 @@ export class ClaworldRelayClient extends EventEmitter {
244
259
  return { status: response.status, body };
245
260
  }
246
261
 
262
+ async requestJsonWithDeliveryVisibilityRetry(pathName, init = {}, fallback = {}) {
263
+ let attempt = 0;
264
+ while (true) {
265
+ const result = await this.requestJson(pathName, init, fallback);
266
+ if (!isDeliveryVisibilityMiss(result) || attempt >= DELIVERY_VISIBILITY_RETRY_ATTEMPTS - 1) {
267
+ return result;
268
+ }
269
+ attempt += 1;
270
+ await waitForDeliveryVisibilityRetry();
271
+ }
272
+ }
273
+
247
274
  async openSocket({
248
275
  wsUrl,
249
276
  agentId,
@@ -879,7 +906,7 @@ export class ClaworldRelayClient extends EventEmitter {
879
906
  replyText,
880
907
  source,
881
908
  });
882
- const result = await this.requestJson(`/v1/deliveries/${encodeURIComponent(envelope.deliveryId)}/reply`, {
909
+ const result = await this.requestJsonWithDeliveryVisibilityRetry(`/v1/deliveries/${encodeURIComponent(envelope.deliveryId)}/reply`, {
883
910
  method: 'POST',
884
911
  headers: buildRuntimeAuthHeaders(this.runtimeConfig, { 'content-type': 'application/json' }),
885
912
  body: JSON.stringify({
@@ -901,7 +928,7 @@ export class ClaworldRelayClient extends EventEmitter {
901
928
 
902
929
  async acceptDeliveryHttp({ deliveryId, sessionKey = null, source = 'runtime_dispatch' } = {}) {
903
930
  const normalizedDeliveryId = normalizeOptionalText(deliveryId);
904
- const result = await this.requestJson(`/v1/deliveries/${encodeURIComponent(normalizedDeliveryId)}/accepted`, {
931
+ const result = await this.requestJsonWithDeliveryVisibilityRetry(`/v1/deliveries/${encodeURIComponent(normalizedDeliveryId)}/accepted`, {
905
932
  method: 'POST',
906
933
  headers: buildRuntimeAuthHeaders(this.runtimeConfig, { 'content-type': 'application/json' }),
907
934
  body: JSON.stringify({
@@ -932,6 +959,10 @@ export class ClaworldRelayClient extends EventEmitter {
932
959
  timeoutMs = DEFAULT_REPLY_ACK_TIMEOUT_MS,
933
960
  httpFallback = true,
934
961
  } = {}) {
962
+ const ackPromise = this.waitForReplyAck({
963
+ deliveryId,
964
+ timeoutMs,
965
+ });
935
966
  const envelope = this.sendReply({
936
967
  deliveryId,
937
968
  sessionKey,
@@ -940,10 +971,7 @@ export class ClaworldRelayClient extends EventEmitter {
940
971
  });
941
972
 
942
973
  try {
943
- const ack = await this.waitForReplyAck({
944
- deliveryId: envelope.deliveryId,
945
- timeoutMs,
946
- });
974
+ const ack = await ackPromise;
947
975
  return {
948
976
  ok: true,
949
977
  envelope,
@@ -1011,6 +1039,24 @@ export class ClaworldRelayClient extends EventEmitter {
1011
1039
  }
1012
1040
  }
1013
1041
 
1042
+ async submitDeliveryReply({
1043
+ deliveryId,
1044
+ sessionKey,
1045
+ replyText,
1046
+ source = 'subagent',
1047
+ timeoutMs = DEFAULT_REPLY_ACK_TIMEOUT_MS,
1048
+ httpFallback = true,
1049
+ } = {}) {
1050
+ return await this.sendReplyAndWaitForAck({
1051
+ deliveryId,
1052
+ sessionKey,
1053
+ replyText,
1054
+ source,
1055
+ timeoutMs,
1056
+ httpFallback,
1057
+ });
1058
+ }
1059
+
1014
1060
  async sendAcceptedAndWaitForAck({
1015
1061
  deliveryId,
1016
1062
  sessionKey,
@@ -1018,6 +1064,10 @@ export class ClaworldRelayClient extends EventEmitter {
1018
1064
  timeoutMs = DEFAULT_REPLY_ACK_TIMEOUT_MS,
1019
1065
  httpFallback = true,
1020
1066
  } = {}) {
1067
+ const ackPromise = this.waitForAcceptedAck({
1068
+ deliveryId,
1069
+ timeoutMs,
1070
+ });
1021
1071
  const envelope = this.sendAccepted({
1022
1072
  deliveryId,
1023
1073
  sessionKey,
@@ -1025,10 +1075,7 @@ export class ClaworldRelayClient extends EventEmitter {
1025
1075
  });
1026
1076
 
1027
1077
  try {
1028
- const ack = await this.waitForAcceptedAck({
1029
- deliveryId: envelope.deliveryId,
1030
- timeoutMs,
1031
- });
1078
+ const ack = await ackPromise;
1032
1079
  return {
1033
1080
  ok: true,
1034
1081
  envelope,
@@ -1082,7 +1129,7 @@ export class ClaworldRelayClient extends EventEmitter {
1082
1129
 
1083
1130
  async keepDeliverySilentHttp({ deliveryId, reason = null, source = 'openclaw-autochain' } = {}) {
1084
1131
  const normalizedDeliveryId = normalizeOptionalText(deliveryId);
1085
- const result = await this.requestJson(`/v1/deliveries/${encodeURIComponent(normalizedDeliveryId)}/kept-silent`, {
1132
+ const result = await this.requestJsonWithDeliveryVisibilityRetry(`/v1/deliveries/${encodeURIComponent(normalizedDeliveryId)}/kept-silent`, {
1086
1133
  method: 'POST',
1087
1134
  headers: buildRuntimeAuthHeaders(this.runtimeConfig, { 'content-type': 'application/json' }),
1088
1135
  body: JSON.stringify({
@@ -1113,6 +1160,10 @@ export class ClaworldRelayClient extends EventEmitter {
1113
1160
  timeoutMs = DEFAULT_REPLY_ACK_TIMEOUT_MS,
1114
1161
  httpFallback = true,
1115
1162
  } = {}) {
1163
+ const ackPromise = this.waitForKeepSilentAck({
1164
+ deliveryId,
1165
+ timeoutMs,
1166
+ });
1116
1167
  const envelope = this.sendKeepSilent({
1117
1168
  deliveryId,
1118
1169
  sessionKey,
@@ -1121,10 +1172,7 @@ export class ClaworldRelayClient extends EventEmitter {
1121
1172
  });
1122
1173
 
1123
1174
  try {
1124
- const ack = await this.waitForKeepSilentAck({
1125
- deliveryId: envelope.deliveryId,
1126
- timeoutMs,
1127
- });
1175
+ const ack = await ackPromise;
1128
1176
  return {
1129
1177
  ok: true,
1130
1178
  envelope,
@@ -1192,6 +1240,24 @@ export class ClaworldRelayClient extends EventEmitter {
1192
1240
  }
1193
1241
  }
1194
1242
 
1243
+ async submitDeliveryKeptSilent({
1244
+ deliveryId,
1245
+ sessionKey,
1246
+ reason = null,
1247
+ source = 'openclaw-autochain',
1248
+ timeoutMs = DEFAULT_REPLY_ACK_TIMEOUT_MS,
1249
+ httpFallback = true,
1250
+ } = {}) {
1251
+ return await this.sendKeepSilentAndWaitForAck({
1252
+ deliveryId,
1253
+ sessionKey,
1254
+ reason,
1255
+ source,
1256
+ timeoutMs,
1257
+ httpFallback,
1258
+ });
1259
+ }
1260
+
1195
1261
  async createChatRequest({ fromAgentId, displayName, agentCode, requestContext = {} } = {}) {
1196
1262
  const normalized = normalizeChatRequestInput({ requestContext, source: 'direct_lookup' });
1197
1263
  const normalizedDisplayName = normalizeOptionalText(displayName);