@xfxstudio/claworld 2026.7.1-testing.2 → 2026.7.7-testing.1
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.
- package/README.md +29 -0
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/skills/claworld-help/SKILL.md +11 -5
- package/skills/claworld-main-session/SKILL.md +1 -1
- package/skills/claworld-management-session/SKILL.md +9 -12
- package/src/openclaw/plugin/account-identity.js +8 -2
- package/src/openclaw/plugin/claworld-channel-plugin.js +21 -77
- package/src/openclaw/plugin/managed-config.js +16 -1
- package/src/openclaw/plugin/register-tooling.js +53 -47
- package/src/openclaw/plugin/register.js +70 -105
- package/src/openclaw/plugin/relay-client.js +209 -70
- package/src/openclaw/plugin-version.js +9 -22
- package/src/openclaw/runtime/tool-contracts.js +2 -2
- package/src/product-shell/contracts/chat-request-approval-policy.js +6 -0
- package/src/product-shell/contracts/search-item.js +2 -3
|
@@ -2,7 +2,10 @@ import { EventEmitter } from 'events';
|
|
|
2
2
|
import WebSocket from 'ws';
|
|
3
3
|
import { resolveClaworldRuntimeConfig } from './config-schema.js';
|
|
4
4
|
import { buildRuntimeAuthHeaders } from './account-identity.js';
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
CLAWORLD_OPENCLAW_PLUGIN_CLIENT,
|
|
7
|
+
CLAWORLD_PLUGIN_CURRENT_VERSION,
|
|
8
|
+
} from '../plugin-version.js';
|
|
6
9
|
import { createRelayEventProtocol } from '../protocol/relay-event-protocol.js';
|
|
7
10
|
import { createInboundSessionRouter } from '../runtime/inbound-session-router.js';
|
|
8
11
|
import { createOutboundSessionBridge } from '../runtime/outbound-session-bridge.js';
|
|
@@ -32,6 +35,10 @@ import {
|
|
|
32
35
|
|
|
33
36
|
const DELIVERY_VISIBILITY_RETRY_ATTEMPTS = 20;
|
|
34
37
|
const DELIVERY_VISIBILITY_RETRY_DELAY_MS = 10;
|
|
38
|
+
const DEFAULT_RELAY_AUTH_TIMEOUT_MS = 30_000;
|
|
39
|
+
const DEFAULT_RECONNECT_BASE_DELAY_MS = 1_000;
|
|
40
|
+
const DEFAULT_RECONNECT_MAX_DELAY_MS = 60_000;
|
|
41
|
+
const DEFAULT_RECONNECT_JITTER_RATIO = 0.2;
|
|
35
42
|
|
|
36
43
|
function isDeliveryVisibilityMiss(result = {}) {
|
|
37
44
|
return Number(result?.status) === 404
|
|
@@ -53,6 +60,11 @@ export class ClaworldRelayClient extends EventEmitter {
|
|
|
53
60
|
protocol = createRelayEventProtocol(),
|
|
54
61
|
wsFactory = (url) => new WebSocket(url),
|
|
55
62
|
httpFetch = globalThis.fetch,
|
|
63
|
+
authTimeoutMs = DEFAULT_RELAY_AUTH_TIMEOUT_MS,
|
|
64
|
+
reconnectBaseDelayMs = DEFAULT_RECONNECT_BASE_DELAY_MS,
|
|
65
|
+
reconnectMaxDelayMs = DEFAULT_RECONNECT_MAX_DELAY_MS,
|
|
66
|
+
reconnectJitterRatio = DEFAULT_RECONNECT_JITTER_RATIO,
|
|
67
|
+
random = Math.random,
|
|
56
68
|
} = {}) {
|
|
57
69
|
super();
|
|
58
70
|
this.logger = logger;
|
|
@@ -61,6 +73,11 @@ export class ClaworldRelayClient extends EventEmitter {
|
|
|
61
73
|
this.protocol = protocol;
|
|
62
74
|
this.wsFactory = wsFactory;
|
|
63
75
|
this.httpFetch = httpFetch;
|
|
76
|
+
this.authTimeoutMs = Math.max(1, Math.floor(Number(authTimeoutMs) || DEFAULT_RELAY_AUTH_TIMEOUT_MS));
|
|
77
|
+
this.reconnectBaseDelayMs = Math.max(1, Math.floor(Number(reconnectBaseDelayMs) || DEFAULT_RECONNECT_BASE_DELAY_MS));
|
|
78
|
+
this.reconnectMaxDelayMs = Math.max(this.reconnectBaseDelayMs, Math.floor(Number(reconnectMaxDelayMs) || DEFAULT_RECONNECT_MAX_DELAY_MS));
|
|
79
|
+
this.reconnectJitterRatio = Math.max(0, Math.min(1, Number(reconnectJitterRatio) || 0));
|
|
80
|
+
this.random = typeof random === 'function' ? random : Math.random;
|
|
64
81
|
this.ws = null;
|
|
65
82
|
this.events = [];
|
|
66
83
|
this.heartbeatTimer = null;
|
|
@@ -103,9 +120,17 @@ export class ClaworldRelayClient extends EventEmitter {
|
|
|
103
120
|
this.reconnectTimer = null;
|
|
104
121
|
}
|
|
105
122
|
|
|
106
|
-
resolveReconnectDelayMs() {
|
|
107
|
-
const
|
|
108
|
-
|
|
123
|
+
resolveReconnectDelayMs(attempt = this.reconnectAttempts + 1) {
|
|
124
|
+
const normalizedAttempt = Math.max(1, Math.floor(Number(attempt) || 1));
|
|
125
|
+
const exponent = Math.min(normalizedAttempt - 1, 10);
|
|
126
|
+
const baseDelayMs = Math.min(
|
|
127
|
+
this.reconnectMaxDelayMs,
|
|
128
|
+
this.reconnectBaseDelayMs * (2 ** exponent),
|
|
129
|
+
);
|
|
130
|
+
const jitterWindowMs = Math.floor(baseDelayMs * this.reconnectJitterRatio);
|
|
131
|
+
if (jitterWindowMs <= 0) return baseDelayMs;
|
|
132
|
+
const jitterMs = Math.floor(Math.max(0, Math.min(1, Number(this.random()) || 0)) * jitterWindowMs);
|
|
133
|
+
return Math.min(this.reconnectMaxDelayMs, baseDelayMs + jitterMs);
|
|
109
134
|
}
|
|
110
135
|
|
|
111
136
|
shouldAutoReconnect(disconnectInfo = null) {
|
|
@@ -285,9 +310,59 @@ export class ClaworldRelayClient extends EventEmitter {
|
|
|
285
310
|
return await new Promise((resolve, reject) => {
|
|
286
311
|
let settled = false;
|
|
287
312
|
let suppressCloseHandler = false;
|
|
313
|
+
let authTimer = null;
|
|
288
314
|
const ws = this.wsFactory(wsUrl);
|
|
289
315
|
this.ws = ws;
|
|
290
316
|
|
|
317
|
+
const clearAuthTimer = () => {
|
|
318
|
+
if (authTimer) clearTimeout(authTimer);
|
|
319
|
+
authTimer = null;
|
|
320
|
+
};
|
|
321
|
+
|
|
322
|
+
const closeSocketAfterFailedAuth = (reason = 'auth_failed') => {
|
|
323
|
+
try {
|
|
324
|
+
if (this.ws === ws) this.ws = null;
|
|
325
|
+
if (ws.readyState !== 3) ws.close(1008, reason);
|
|
326
|
+
} catch {
|
|
327
|
+
// No-op.
|
|
328
|
+
}
|
|
329
|
+
};
|
|
330
|
+
|
|
331
|
+
const settleAuthResolve = (message) => {
|
|
332
|
+
if (settled) return;
|
|
333
|
+
settled = true;
|
|
334
|
+
clearAuthTimer();
|
|
335
|
+
resolve(message);
|
|
336
|
+
};
|
|
337
|
+
|
|
338
|
+
const settleAuthReject = (error, { suppressClose = false, closeReason = null } = {}) => {
|
|
339
|
+
if (settled) return;
|
|
340
|
+
settled = true;
|
|
341
|
+
clearAuthTimer();
|
|
342
|
+
if (suppressClose) suppressCloseHandler = true;
|
|
343
|
+
this.connectionState = 'error';
|
|
344
|
+
if (closeReason) closeSocketAfterFailedAuth(closeReason);
|
|
345
|
+
reject(error);
|
|
346
|
+
};
|
|
347
|
+
|
|
348
|
+
authTimer = setTimeout(() => {
|
|
349
|
+
const timeoutError = createRuntimeBoundaryError({
|
|
350
|
+
code: 'relay_auth_timeout',
|
|
351
|
+
category: 'transport',
|
|
352
|
+
status: 504,
|
|
353
|
+
message: `timed out waiting for relay authentication after ${this.authTimeoutMs}ms`,
|
|
354
|
+
publicMessage: 'relay authentication timed out',
|
|
355
|
+
recoverable: true,
|
|
356
|
+
context: this.buildBoundaryContext({
|
|
357
|
+
stage: 'auth',
|
|
358
|
+
timeoutMs: this.authTimeoutMs,
|
|
359
|
+
}),
|
|
360
|
+
});
|
|
361
|
+
timeoutError.reason = 'auth_timeout';
|
|
362
|
+
timeoutError.close = this.buildDisconnectInfo(null, 'auth_timeout', 'auth');
|
|
363
|
+
settleAuthReject(timeoutError, { suppressClose: true, closeReason: 'auth_timeout' });
|
|
364
|
+
}, this.authTimeoutMs);
|
|
365
|
+
|
|
291
366
|
ws.on('open', () => {
|
|
292
367
|
this.logger.info?.('[claworld:relay-client] websocket open, sending auth', {
|
|
293
368
|
accountId: this.runtimeConfig.accountId,
|
|
@@ -295,13 +370,18 @@ export class ClaworldRelayClient extends EventEmitter {
|
|
|
295
370
|
clientVersion,
|
|
296
371
|
bridgeProtocol: this.protocol.version,
|
|
297
372
|
});
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
373
|
+
try {
|
|
374
|
+
this.send({
|
|
375
|
+
type: 'auth',
|
|
376
|
+
agentId,
|
|
377
|
+
credential,
|
|
378
|
+
client: CLAWORLD_OPENCLAW_PLUGIN_CLIENT,
|
|
379
|
+
clientVersion,
|
|
380
|
+
bridgeProtocol: this.protocol.version,
|
|
381
|
+
});
|
|
382
|
+
} catch (error) {
|
|
383
|
+
settleAuthReject(error, { suppressClose: true, closeReason: 'auth_send_failed' });
|
|
384
|
+
}
|
|
305
385
|
});
|
|
306
386
|
|
|
307
387
|
ws.on('message', (buf) => {
|
|
@@ -316,9 +396,7 @@ export class ClaworldRelayClient extends EventEmitter {
|
|
|
316
396
|
context: { stage: 'message_parse' },
|
|
317
397
|
});
|
|
318
398
|
if (!settled) {
|
|
319
|
-
|
|
320
|
-
this.connectionState = 'error';
|
|
321
|
-
reject(normalized);
|
|
399
|
+
settleAuthReject(normalized, { suppressClose: true, closeReason: 'message_parse_failed' });
|
|
322
400
|
}
|
|
323
401
|
return;
|
|
324
402
|
}
|
|
@@ -327,7 +405,6 @@ export class ClaworldRelayClient extends EventEmitter {
|
|
|
327
405
|
this.emitRelayMessage(message, { sessionTarget, fallbackTarget });
|
|
328
406
|
|
|
329
407
|
if (message.event === 'auth.ok' && !settled) {
|
|
330
|
-
settled = true;
|
|
331
408
|
this.connectionState = 'authenticated';
|
|
332
409
|
this.reconnectAttempts = 0;
|
|
333
410
|
this.logger.info?.('[claworld:relay-client] auth ok', {
|
|
@@ -335,13 +412,10 @@ export class ClaworldRelayClient extends EventEmitter {
|
|
|
335
412
|
agentId,
|
|
336
413
|
});
|
|
337
414
|
this.startHeartbeatLoop();
|
|
338
|
-
|
|
415
|
+
settleAuthResolve(message);
|
|
339
416
|
}
|
|
340
417
|
|
|
341
418
|
if (message.event === 'error' && !settled && message.data?.code === 'unauthorized') {
|
|
342
|
-
settled = true;
|
|
343
|
-
suppressCloseHandler = true;
|
|
344
|
-
this.connectionState = 'error';
|
|
345
419
|
const authReason = message.data?.reason || message.data?.error || 'unauthorized';
|
|
346
420
|
const authError = createRuntimeBoundaryError({
|
|
347
421
|
code: message.data?.code || 'unauthorized',
|
|
@@ -364,7 +438,7 @@ export class ClaworldRelayClient extends EventEmitter {
|
|
|
364
438
|
error: authError.message,
|
|
365
439
|
code: authError.code,
|
|
366
440
|
});
|
|
367
|
-
|
|
441
|
+
settleAuthReject(authError, { suppressClose: true, closeReason: authReason });
|
|
368
442
|
}
|
|
369
443
|
} catch (error) {
|
|
370
444
|
const normalized = this.emitBoundaryError('[claworld:relay-client] relay message handling failed', error, {
|
|
@@ -377,10 +451,7 @@ export class ClaworldRelayClient extends EventEmitter {
|
|
|
377
451
|
},
|
|
378
452
|
});
|
|
379
453
|
if (!settled) {
|
|
380
|
-
|
|
381
|
-
suppressCloseHandler = true;
|
|
382
|
-
this.connectionState = 'error';
|
|
383
|
-
reject(normalized);
|
|
454
|
+
settleAuthReject(normalized, { suppressClose: true, closeReason: 'message_handling_failed' });
|
|
384
455
|
}
|
|
385
456
|
}
|
|
386
457
|
});
|
|
@@ -402,6 +473,7 @@ export class ClaworldRelayClient extends EventEmitter {
|
|
|
402
473
|
|
|
403
474
|
if (!settled) {
|
|
404
475
|
settled = true;
|
|
476
|
+
clearAuthTimer();
|
|
405
477
|
this.connectionState = disconnectInfo.reason === 'duplicate_connection_replaced' ? 'replaced' : 'error';
|
|
406
478
|
reject(this.createClosedBeforeAuthError(disconnectInfo));
|
|
407
479
|
return;
|
|
@@ -430,11 +502,8 @@ export class ClaworldRelayClient extends EventEmitter {
|
|
|
430
502
|
},
|
|
431
503
|
});
|
|
432
504
|
if (!settled) {
|
|
433
|
-
settled = true;
|
|
434
|
-
suppressCloseHandler = true;
|
|
435
|
-
this.connectionState = 'error';
|
|
436
505
|
normalized.reason = normalized.reason || normalized.code || normalized.message;
|
|
437
|
-
|
|
506
|
+
settleAuthReject(normalized, { suppressClose: true, closeReason: 'connect_error' });
|
|
438
507
|
}
|
|
439
508
|
});
|
|
440
509
|
});
|
|
@@ -448,8 +517,8 @@ export class ClaworldRelayClient extends EventEmitter {
|
|
|
448
517
|
}
|
|
449
518
|
|
|
450
519
|
this.clearReconnectTimer();
|
|
451
|
-
const delayMs = this.resolveReconnectDelayMs();
|
|
452
520
|
const attempt = this.reconnectAttempts + 1;
|
|
521
|
+
const delayMs = this.resolveReconnectDelayMs(attempt);
|
|
453
522
|
this.reconnectAttempts = attempt;
|
|
454
523
|
this.logger.warn?.('[claworld:relay-client] scheduling reconnect', {
|
|
455
524
|
accountId: this.runtimeConfig?.accountId || null,
|
|
@@ -487,7 +556,7 @@ export class ClaworldRelayClient extends EventEmitter {
|
|
|
487
556
|
config,
|
|
488
557
|
agentId,
|
|
489
558
|
credential = null,
|
|
490
|
-
clientVersion =
|
|
559
|
+
clientVersion = CLAWORLD_PLUGIN_CURRENT_VERSION,
|
|
491
560
|
sessionTarget,
|
|
492
561
|
fallbackTarget,
|
|
493
562
|
} = {}) {
|
|
@@ -793,7 +862,7 @@ export class ClaworldRelayClient extends EventEmitter {
|
|
|
793
862
|
});
|
|
794
863
|
}
|
|
795
864
|
|
|
796
|
-
waitForAcceptedAck({ deliveryId, timeoutMs = DEFAULT_REPLY_ACK_TIMEOUT_MS } = {}) {
|
|
865
|
+
waitForAcceptedAck({ deliveryId, timeoutMs = DEFAULT_REPLY_ACK_TIMEOUT_MS, signal = null } = {}) {
|
|
797
866
|
const normalizedDeliveryId = normalizeOptionalText(deliveryId);
|
|
798
867
|
if (!normalizedDeliveryId) {
|
|
799
868
|
return Promise.reject(createRuntimeBoundaryError({
|
|
@@ -805,6 +874,20 @@ export class ClaworldRelayClient extends EventEmitter {
|
|
|
805
874
|
recoverable: true,
|
|
806
875
|
}));
|
|
807
876
|
}
|
|
877
|
+
if (signal?.aborted) {
|
|
878
|
+
return Promise.reject(createRuntimeBoundaryError({
|
|
879
|
+
code: 'relay_delivery_accept_ack_wait_cancelled',
|
|
880
|
+
category: 'transport',
|
|
881
|
+
status: 499,
|
|
882
|
+
message: `relay delivery acceptance acknowledgement wait cancelled for ${normalizedDeliveryId}`,
|
|
883
|
+
publicMessage: 'relay delivery acceptance acknowledgement wait cancelled',
|
|
884
|
+
recoverable: true,
|
|
885
|
+
context: this.buildBoundaryContext({
|
|
886
|
+
stage: 'delivery_accept_ack_wait',
|
|
887
|
+
deliveryId: normalizedDeliveryId,
|
|
888
|
+
}),
|
|
889
|
+
}));
|
|
890
|
+
}
|
|
808
891
|
|
|
809
892
|
return new Promise((resolve, reject) => {
|
|
810
893
|
let settled = false;
|
|
@@ -815,6 +898,7 @@ export class ClaworldRelayClient extends EventEmitter {
|
|
|
815
898
|
this.off('delivery.accepted', onAccepted);
|
|
816
899
|
this.off('disconnect', onDisconnect);
|
|
817
900
|
this.off('close', onDisconnect);
|
|
901
|
+
signal?.removeEventListener('abort', onAbort);
|
|
818
902
|
};
|
|
819
903
|
|
|
820
904
|
const settleResolve = (value) => {
|
|
@@ -854,9 +938,25 @@ export class ClaworldRelayClient extends EventEmitter {
|
|
|
854
938
|
}));
|
|
855
939
|
};
|
|
856
940
|
|
|
941
|
+
const onAbort = () => {
|
|
942
|
+
settleReject(createRuntimeBoundaryError({
|
|
943
|
+
code: 'relay_delivery_accept_ack_wait_cancelled',
|
|
944
|
+
category: 'transport',
|
|
945
|
+
status: 499,
|
|
946
|
+
message: `relay delivery acceptance acknowledgement wait cancelled for ${normalizedDeliveryId}`,
|
|
947
|
+
publicMessage: 'relay delivery acceptance acknowledgement wait cancelled',
|
|
948
|
+
recoverable: true,
|
|
949
|
+
context: this.buildBoundaryContext({
|
|
950
|
+
stage: 'delivery_accept_ack_wait',
|
|
951
|
+
deliveryId: normalizedDeliveryId,
|
|
952
|
+
}),
|
|
953
|
+
}));
|
|
954
|
+
};
|
|
955
|
+
|
|
857
956
|
this.on('delivery.accepted', onAccepted);
|
|
858
957
|
this.on('disconnect', onDisconnect);
|
|
859
958
|
this.on('close', onDisconnect);
|
|
959
|
+
signal?.addEventListener('abort', onAbort, { once: true });
|
|
860
960
|
|
|
861
961
|
timeout = setTimeout(() => {
|
|
862
962
|
settleReject(buildAcceptedAckTimeoutError({
|
|
@@ -1097,6 +1197,52 @@ export class ClaworldRelayClient extends EventEmitter {
|
|
|
1097
1197
|
};
|
|
1098
1198
|
}
|
|
1099
1199
|
|
|
1200
|
+
async submitAcceptedHttpFallback({
|
|
1201
|
+
deliveryId,
|
|
1202
|
+
sessionKey,
|
|
1203
|
+
source = 'runtime_dispatch',
|
|
1204
|
+
error = null,
|
|
1205
|
+
} = {}) {
|
|
1206
|
+
this.logger.warn?.('[claworld:relay-client] delivery acceptance websocket transport failed; attempting HTTP fallback', {
|
|
1207
|
+
accountId: this.runtimeConfig?.accountId || null,
|
|
1208
|
+
agentId: this.boundAgentId,
|
|
1209
|
+
deliveryId: normalizeOptionalText(deliveryId),
|
|
1210
|
+
sessionKey: normalizeOptionalText(sessionKey) || null,
|
|
1211
|
+
error: error?.message || String(error),
|
|
1212
|
+
});
|
|
1213
|
+
|
|
1214
|
+
const fallbackResult = await this.acceptDeliveryHttp({
|
|
1215
|
+
deliveryId,
|
|
1216
|
+
sessionKey,
|
|
1217
|
+
source,
|
|
1218
|
+
});
|
|
1219
|
+
|
|
1220
|
+
if (fallbackResult.status >= 200 && fallbackResult.status < 300) {
|
|
1221
|
+
return {
|
|
1222
|
+
ok: true,
|
|
1223
|
+
envelope: fallbackResult.envelope,
|
|
1224
|
+
ack: {
|
|
1225
|
+
event: 'delivery.accepted',
|
|
1226
|
+
data: fallbackResult.body,
|
|
1227
|
+
},
|
|
1228
|
+
transport: 'http',
|
|
1229
|
+
fallbackUsed: true,
|
|
1230
|
+
};
|
|
1231
|
+
}
|
|
1232
|
+
|
|
1233
|
+
throw buildReplyFallbackError({
|
|
1234
|
+
deliveryId: fallbackResult.envelope.deliveryId,
|
|
1235
|
+
status: fallbackResult.status,
|
|
1236
|
+
body: fallbackResult.body,
|
|
1237
|
+
context: this.buildBoundaryContext({
|
|
1238
|
+
stage: 'delivery_accept_fallback',
|
|
1239
|
+
deliveryId: fallbackResult.envelope.deliveryId,
|
|
1240
|
+
sessionKey: normalizeOptionalText(sessionKey) || null,
|
|
1241
|
+
fallbackFrom: error?.code || error?.message || null,
|
|
1242
|
+
}),
|
|
1243
|
+
});
|
|
1244
|
+
}
|
|
1245
|
+
|
|
1100
1246
|
async sendReplyAndWaitForAck({
|
|
1101
1247
|
deliveryId,
|
|
1102
1248
|
sessionKey,
|
|
@@ -1190,15 +1336,40 @@ export class ClaworldRelayClient extends EventEmitter {
|
|
|
1190
1336
|
timeoutMs = DEFAULT_REPLY_ACK_TIMEOUT_MS,
|
|
1191
1337
|
httpFallback = true,
|
|
1192
1338
|
} = {}) {
|
|
1339
|
+
if (httpFallback && (!this.ws || this.ws.readyState !== 1)) {
|
|
1340
|
+
return await this.submitAcceptedHttpFallback({
|
|
1341
|
+
deliveryId,
|
|
1342
|
+
sessionKey,
|
|
1343
|
+
source,
|
|
1344
|
+
error: this.buildWsNotConnectedError('delivery_accept_send'),
|
|
1345
|
+
});
|
|
1346
|
+
}
|
|
1347
|
+
|
|
1348
|
+
const ackAbortController = new AbortController();
|
|
1193
1349
|
const ackPromise = this.waitForAcceptedAck({
|
|
1194
1350
|
deliveryId,
|
|
1195
1351
|
timeoutMs,
|
|
1352
|
+
signal: ackAbortController.signal,
|
|
1196
1353
|
});
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1354
|
+
let envelope;
|
|
1355
|
+
|
|
1356
|
+
try {
|
|
1357
|
+
envelope = this.sendAccepted({
|
|
1358
|
+
deliveryId,
|
|
1359
|
+
sessionKey,
|
|
1360
|
+
source,
|
|
1361
|
+
});
|
|
1362
|
+
} catch (error) {
|
|
1363
|
+
ackAbortController.abort();
|
|
1364
|
+
void ackPromise.catch(() => {});
|
|
1365
|
+
if (!httpFallback) throw error;
|
|
1366
|
+
return await this.submitAcceptedHttpFallback({
|
|
1367
|
+
deliveryId,
|
|
1368
|
+
sessionKey,
|
|
1369
|
+
source,
|
|
1370
|
+
error,
|
|
1371
|
+
});
|
|
1372
|
+
}
|
|
1202
1373
|
|
|
1203
1374
|
try {
|
|
1204
1375
|
const ack = await ackPromise;
|
|
@@ -1212,43 +1383,11 @@ export class ClaworldRelayClient extends EventEmitter {
|
|
|
1212
1383
|
} catch (error) {
|
|
1213
1384
|
if (!httpFallback) throw error;
|
|
1214
1385
|
|
|
1215
|
-
this.
|
|
1216
|
-
accountId: this.runtimeConfig?.accountId || null,
|
|
1217
|
-
agentId: this.boundAgentId,
|
|
1218
|
-
deliveryId: envelope.deliveryId,
|
|
1219
|
-
sessionKey: envelope.sessionKey,
|
|
1220
|
-
error: error?.message || String(error),
|
|
1221
|
-
});
|
|
1222
|
-
|
|
1223
|
-
const fallbackResult = await this.acceptDeliveryHttp({
|
|
1386
|
+
return await this.submitAcceptedHttpFallback({
|
|
1224
1387
|
deliveryId: envelope.deliveryId,
|
|
1225
1388
|
sessionKey: envelope.sessionKey,
|
|
1226
1389
|
source: envelope.source,
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
if (fallbackResult.status >= 200 && fallbackResult.status < 300) {
|
|
1230
|
-
return {
|
|
1231
|
-
ok: true,
|
|
1232
|
-
envelope,
|
|
1233
|
-
ack: {
|
|
1234
|
-
event: 'delivery.accepted',
|
|
1235
|
-
data: fallbackResult.body,
|
|
1236
|
-
},
|
|
1237
|
-
transport: 'http',
|
|
1238
|
-
fallbackUsed: true,
|
|
1239
|
-
};
|
|
1240
|
-
}
|
|
1241
|
-
|
|
1242
|
-
throw buildReplyFallbackError({
|
|
1243
|
-
deliveryId: envelope.deliveryId,
|
|
1244
|
-
status: fallbackResult.status,
|
|
1245
|
-
body: fallbackResult.body,
|
|
1246
|
-
context: this.buildBoundaryContext({
|
|
1247
|
-
stage: 'delivery_accept_fallback',
|
|
1248
|
-
deliveryId: envelope.deliveryId,
|
|
1249
|
-
sessionKey: envelope.sessionKey,
|
|
1250
|
-
fallbackFrom: error?.code || error?.message || null,
|
|
1251
|
-
}),
|
|
1390
|
+
error,
|
|
1252
1391
|
});
|
|
1253
1392
|
}
|
|
1254
1393
|
}
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import repoPackageJson from '../../package.json' with { type: 'json' };
|
|
2
2
|
|
|
3
3
|
export const CLAWORLD_PLUGIN_PACKAGE_NAME = '@xfxstudio/claworld';
|
|
4
|
-
export const
|
|
4
|
+
export const CLAWORLD_CLIENT_HEADER = 'x-claworld-client';
|
|
5
|
+
export const CLAWORLD_CLIENT_VERSION_HEADER = 'x-claworld-client-version';
|
|
6
|
+
export const CLAWORLD_CLIENT_CHANNEL_HEADER = 'x-claworld-client-channel';
|
|
7
|
+
export const CLAWORLD_OPENCLAW_PLUGIN_CLIENT = 'openclaw-plugin';
|
|
5
8
|
|
|
6
9
|
function normalizeText(value, fallback = null) {
|
|
7
10
|
if (value == null) return fallback;
|
|
@@ -9,15 +12,6 @@ function normalizeText(value, fallback = null) {
|
|
|
9
12
|
return normalized || fallback;
|
|
10
13
|
}
|
|
11
14
|
|
|
12
|
-
function normalizeHeaderValue(value) {
|
|
13
|
-
if (Array.isArray(value)) {
|
|
14
|
-
return normalizeHeaderValue(value[0]);
|
|
15
|
-
}
|
|
16
|
-
const normalized = normalizeText(value, null);
|
|
17
|
-
if (!normalized) return null;
|
|
18
|
-
return normalized.split(',')[0]?.trim() || null;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
15
|
export function normalizeClaworldPluginVersion(value, fallback = null) {
|
|
22
16
|
const normalized = normalizeText(value, null);
|
|
23
17
|
if (!normalized) return fallback;
|
|
@@ -39,16 +33,9 @@ function resolveCurrentPluginVersion() {
|
|
|
39
33
|
|
|
40
34
|
export const CLAWORLD_PLUGIN_CURRENT_VERSION = resolveCurrentPluginVersion();
|
|
41
35
|
|
|
42
|
-
export function
|
|
43
|
-
const
|
|
44
|
-
return
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
normalizedVersion: normalizeClaworldPluginVersion(rawVersion, null),
|
|
48
|
-
source: rawVersion ? CLAWORLD_PLUGIN_VERSION_HEADER : null,
|
|
49
|
-
};
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
export function buildClaworldRelayClientVersion(version = CLAWORLD_PLUGIN_CURRENT_VERSION) {
|
|
53
|
-
return `claworld-plugin/${normalizeClaworldPluginVersion(version, CLAWORLD_PLUGIN_CURRENT_VERSION)}`;
|
|
36
|
+
export function inferClaworldClientChannel(version = CLAWORLD_PLUGIN_CURRENT_VERSION, fallback = null) {
|
|
37
|
+
const normalized = normalizeClaworldPluginVersion(version, null);
|
|
38
|
+
if (!normalized) return fallback;
|
|
39
|
+
if (/-testing(?:\.|$)/.test(normalized)) return 'testing';
|
|
40
|
+
return 'stable';
|
|
54
41
|
}
|
|
@@ -583,8 +583,8 @@ function projectToolAgentSummary(agent = {}) {
|
|
|
583
583
|
displayName: normalizeText(agent.displayName, null),
|
|
584
584
|
identity: normalizeText(agent.publicIdentity?.displayIdentity, null),
|
|
585
585
|
online: agent.online === true,
|
|
586
|
-
|
|
587
|
-
|
|
586
|
+
visibilityMode: normalizeText(agent.visibilityMode, null),
|
|
587
|
+
contactPolicy: normalizeText(agent.contactPolicy, null),
|
|
588
588
|
};
|
|
589
589
|
}
|
|
590
590
|
|
|
@@ -5,6 +5,7 @@ const SUPPORTED_MODES = Object.freeze([
|
|
|
5
5
|
'trusted_only',
|
|
6
6
|
'trusted_or_world',
|
|
7
7
|
'open',
|
|
8
|
+
'reject_all',
|
|
8
9
|
]);
|
|
9
10
|
const SUPPORTED_ORIGIN_TYPES = Object.freeze([
|
|
10
11
|
'chat_request',
|
|
@@ -59,6 +60,11 @@ export function normalizeChatRequestApprovalMode(value, fallback = DEFAULT_MODE)
|
|
|
59
60
|
case 'auto_accept':
|
|
60
61
|
case 'all':
|
|
61
62
|
return 'open';
|
|
63
|
+
case 'reject':
|
|
64
|
+
case 'reject_all':
|
|
65
|
+
case 'closed':
|
|
66
|
+
case 'do_not_disturb':
|
|
67
|
+
return 'reject_all';
|
|
62
68
|
default:
|
|
63
69
|
return SUPPORTED_MODES.includes(fallback) ? fallback : DEFAULT_MODE;
|
|
64
70
|
}
|
|
@@ -37,9 +37,8 @@ export const PUBLIC_TOOL_ACTION_CATALOG = Object.freeze({
|
|
|
37
37
|
'update_display_name',
|
|
38
38
|
'update_human_profile',
|
|
39
39
|
'update_agent_profile',
|
|
40
|
-
'
|
|
41
|
-
'
|
|
42
|
-
'set_chat_policy',
|
|
40
|
+
'set_visibility_mode',
|
|
41
|
+
'set_contact_policy',
|
|
43
42
|
'set_proactivity',
|
|
44
43
|
'subscribe_person',
|
|
45
44
|
'unsubscribe_person',
|