@xfxstudio/claworld 2026.4.30-testing.2 → 2026.4.30-testing.3
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/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/skills/claworld-a2a-channel-agent/SKILL.md +3 -6
- package/src/openclaw/plugin/claworld-channel-plugin.js +96 -533
- package/src/openclaw/plugin/register-tooling.js +25 -5
- package/src/openclaw/plugin/register.js +41 -50
- package/src/openclaw/plugin/relay-client-shared.js +96 -50
- package/src/openclaw/plugin/relay-client.js +275 -123
- package/src/openclaw/protocol/relay-event-protocol.js +6 -16
- package/src/openclaw/runtime/session-routing.js +7 -3
- package/src/openclaw/runtime/working-memory.js +54 -877
|
@@ -557,19 +557,23 @@ export class ClaworldRelayClient extends EventEmitter {
|
|
|
557
557
|
this.heartbeatTimer = null;
|
|
558
558
|
}
|
|
559
559
|
|
|
560
|
+
buildWsNotConnectedError(stage = 'send') {
|
|
561
|
+
return createRuntimeBoundaryError({
|
|
562
|
+
code: 'relay_ws_not_connected',
|
|
563
|
+
category: 'transport',
|
|
564
|
+
status: 409,
|
|
565
|
+
message: 'relay websocket is not connected',
|
|
566
|
+
publicMessage: 'relay websocket is not connected',
|
|
567
|
+
recoverable: true,
|
|
568
|
+
context: this.buildBoundaryContext({
|
|
569
|
+
stage,
|
|
570
|
+
}),
|
|
571
|
+
});
|
|
572
|
+
}
|
|
573
|
+
|
|
560
574
|
send(payload) {
|
|
561
575
|
if (!this.ws || this.ws.readyState !== 1) {
|
|
562
|
-
throw
|
|
563
|
-
code: 'relay_ws_not_connected',
|
|
564
|
-
category: 'transport',
|
|
565
|
-
status: 409,
|
|
566
|
-
message: 'relay websocket is not connected',
|
|
567
|
-
publicMessage: 'relay websocket is not connected',
|
|
568
|
-
recoverable: true,
|
|
569
|
-
context: this.buildBoundaryContext({
|
|
570
|
-
stage: 'send',
|
|
571
|
-
}),
|
|
572
|
-
});
|
|
576
|
+
throw this.buildWsNotConnectedError('send');
|
|
573
577
|
}
|
|
574
578
|
this.ws.send(JSON.stringify(payload));
|
|
575
579
|
}
|
|
@@ -663,7 +667,7 @@ export class ClaworldRelayClient extends EventEmitter {
|
|
|
663
667
|
return envelope;
|
|
664
668
|
}
|
|
665
669
|
|
|
666
|
-
waitForReplyAck({ deliveryId, timeoutMs = DEFAULT_REPLY_ACK_TIMEOUT_MS } = {}) {
|
|
670
|
+
waitForReplyAck({ deliveryId, timeoutMs = DEFAULT_REPLY_ACK_TIMEOUT_MS, signal = null } = {}) {
|
|
667
671
|
const normalizedDeliveryId = normalizeOptionalText(deliveryId);
|
|
668
672
|
if (!normalizedDeliveryId) {
|
|
669
673
|
return Promise.reject(createRuntimeBoundaryError({
|
|
@@ -675,6 +679,20 @@ export class ClaworldRelayClient extends EventEmitter {
|
|
|
675
679
|
recoverable: true,
|
|
676
680
|
}));
|
|
677
681
|
}
|
|
682
|
+
if (signal?.aborted) {
|
|
683
|
+
return Promise.reject(createRuntimeBoundaryError({
|
|
684
|
+
code: 'relay_reply_ack_wait_cancelled',
|
|
685
|
+
category: 'transport',
|
|
686
|
+
status: 499,
|
|
687
|
+
message: `relay reply acknowledgement wait cancelled for ${normalizedDeliveryId}`,
|
|
688
|
+
publicMessage: 'relay reply acknowledgement wait cancelled',
|
|
689
|
+
recoverable: true,
|
|
690
|
+
context: this.buildBoundaryContext({
|
|
691
|
+
stage: 'reply_ack_wait',
|
|
692
|
+
deliveryId: normalizedDeliveryId,
|
|
693
|
+
}),
|
|
694
|
+
}));
|
|
695
|
+
}
|
|
678
696
|
|
|
679
697
|
return new Promise((resolve, reject) => {
|
|
680
698
|
let settled = false;
|
|
@@ -686,6 +704,7 @@ export class ClaworldRelayClient extends EventEmitter {
|
|
|
686
704
|
this.off('command.accepted', onCommandAccepted);
|
|
687
705
|
this.off('disconnect', onDisconnect);
|
|
688
706
|
this.off('close', onDisconnect);
|
|
707
|
+
signal?.removeEventListener('abort', onAbort);
|
|
689
708
|
};
|
|
690
709
|
|
|
691
710
|
const settleResolve = (value) => {
|
|
@@ -739,10 +758,26 @@ export class ClaworldRelayClient extends EventEmitter {
|
|
|
739
758
|
}));
|
|
740
759
|
};
|
|
741
760
|
|
|
761
|
+
const onAbort = () => {
|
|
762
|
+
settleReject(createRuntimeBoundaryError({
|
|
763
|
+
code: 'relay_reply_ack_wait_cancelled',
|
|
764
|
+
category: 'transport',
|
|
765
|
+
status: 499,
|
|
766
|
+
message: `relay reply acknowledgement wait cancelled for ${normalizedDeliveryId}`,
|
|
767
|
+
publicMessage: 'relay reply acknowledgement wait cancelled',
|
|
768
|
+
recoverable: true,
|
|
769
|
+
context: this.buildBoundaryContext({
|
|
770
|
+
stage: 'reply_ack_wait',
|
|
771
|
+
deliveryId: normalizedDeliveryId,
|
|
772
|
+
}),
|
|
773
|
+
}));
|
|
774
|
+
};
|
|
775
|
+
|
|
742
776
|
this.on('reply.accepted', onReplyAccepted);
|
|
743
777
|
this.on('command.accepted', onCommandAccepted);
|
|
744
778
|
this.on('disconnect', onDisconnect);
|
|
745
779
|
this.on('close', onDisconnect);
|
|
780
|
+
signal?.addEventListener('abort', onAbort, { once: true });
|
|
746
781
|
|
|
747
782
|
timeout = setTimeout(() => {
|
|
748
783
|
settleReject(buildReplyAckTimeoutError({
|
|
@@ -837,7 +872,7 @@ export class ClaworldRelayClient extends EventEmitter {
|
|
|
837
872
|
});
|
|
838
873
|
}
|
|
839
874
|
|
|
840
|
-
waitForKeepSilentAck({ deliveryId, timeoutMs = DEFAULT_REPLY_ACK_TIMEOUT_MS } = {}) {
|
|
875
|
+
waitForKeepSilentAck({ deliveryId, timeoutMs = DEFAULT_REPLY_ACK_TIMEOUT_MS, signal = null } = {}) {
|
|
841
876
|
const normalizedDeliveryId = normalizeOptionalText(deliveryId);
|
|
842
877
|
if (!normalizedDeliveryId) {
|
|
843
878
|
return Promise.reject(createRuntimeBoundaryError({
|
|
@@ -849,6 +884,20 @@ export class ClaworldRelayClient extends EventEmitter {
|
|
|
849
884
|
recoverable: true,
|
|
850
885
|
}));
|
|
851
886
|
}
|
|
887
|
+
if (signal?.aborted) {
|
|
888
|
+
return Promise.reject(createRuntimeBoundaryError({
|
|
889
|
+
code: 'relay_kept_silent_ack_wait_cancelled',
|
|
890
|
+
category: 'transport',
|
|
891
|
+
status: 499,
|
|
892
|
+
message: `relay kept_silent acknowledgement wait cancelled for ${normalizedDeliveryId}`,
|
|
893
|
+
publicMessage: 'relay kept_silent acknowledgement wait cancelled',
|
|
894
|
+
recoverable: true,
|
|
895
|
+
context: this.buildBoundaryContext({
|
|
896
|
+
stage: 'kept_silent_ack_wait',
|
|
897
|
+
deliveryId: normalizedDeliveryId,
|
|
898
|
+
}),
|
|
899
|
+
}));
|
|
900
|
+
}
|
|
852
901
|
|
|
853
902
|
return new Promise((resolve, reject) => {
|
|
854
903
|
let settled = false;
|
|
@@ -859,6 +908,7 @@ export class ClaworldRelayClient extends EventEmitter {
|
|
|
859
908
|
this.off('kept_silent.accepted', onKeptSilentAccepted);
|
|
860
909
|
this.off('disconnect', onDisconnect);
|
|
861
910
|
this.off('close', onDisconnect);
|
|
911
|
+
signal?.removeEventListener('abort', onAbort);
|
|
862
912
|
};
|
|
863
913
|
|
|
864
914
|
const settleResolve = (value) => {
|
|
@@ -898,9 +948,25 @@ export class ClaworldRelayClient extends EventEmitter {
|
|
|
898
948
|
}));
|
|
899
949
|
};
|
|
900
950
|
|
|
951
|
+
const onAbort = () => {
|
|
952
|
+
settleReject(createRuntimeBoundaryError({
|
|
953
|
+
code: 'relay_kept_silent_ack_wait_cancelled',
|
|
954
|
+
category: 'transport',
|
|
955
|
+
status: 499,
|
|
956
|
+
message: `relay kept_silent acknowledgement wait cancelled for ${normalizedDeliveryId}`,
|
|
957
|
+
publicMessage: 'relay kept_silent acknowledgement wait cancelled',
|
|
958
|
+
recoverable: true,
|
|
959
|
+
context: this.buildBoundaryContext({
|
|
960
|
+
stage: 'kept_silent_ack_wait',
|
|
961
|
+
deliveryId: normalizedDeliveryId,
|
|
962
|
+
}),
|
|
963
|
+
}));
|
|
964
|
+
};
|
|
965
|
+
|
|
901
966
|
this.on('kept_silent.accepted', onKeptSilentAccepted);
|
|
902
967
|
this.on('disconnect', onDisconnect);
|
|
903
968
|
this.on('close', onDisconnect);
|
|
969
|
+
signal?.addEventListener('abort', onAbort, { once: true });
|
|
904
970
|
|
|
905
971
|
timeout = setTimeout(() => {
|
|
906
972
|
settleReject(buildKeepSilentAckTimeoutError({
|
|
@@ -943,6 +1009,69 @@ export class ClaworldRelayClient extends EventEmitter {
|
|
|
943
1009
|
};
|
|
944
1010
|
}
|
|
945
1011
|
|
|
1012
|
+
async submitReplyHttpFallback({
|
|
1013
|
+
deliveryId,
|
|
1014
|
+
sessionKey,
|
|
1015
|
+
replyText,
|
|
1016
|
+
source = 'subagent',
|
|
1017
|
+
error = null,
|
|
1018
|
+
} = {}) {
|
|
1019
|
+
this.logger.warn?.('[claworld:relay-client] reply websocket transport failed; attempting HTTP fallback', {
|
|
1020
|
+
accountId: this.runtimeConfig?.accountId || null,
|
|
1021
|
+
agentId: this.boundAgentId,
|
|
1022
|
+
deliveryId: normalizeOptionalText(deliveryId),
|
|
1023
|
+
sessionKey: normalizeOptionalText(sessionKey) || null,
|
|
1024
|
+
error: error?.message || String(error),
|
|
1025
|
+
});
|
|
1026
|
+
|
|
1027
|
+
const fallbackResult = await this.replyToDeliveryHttp({
|
|
1028
|
+
deliveryId,
|
|
1029
|
+
replyText,
|
|
1030
|
+
source,
|
|
1031
|
+
});
|
|
1032
|
+
|
|
1033
|
+
if (fallbackResult.status >= 200 && fallbackResult.status < 300) {
|
|
1034
|
+
return {
|
|
1035
|
+
ok: true,
|
|
1036
|
+
envelope: fallbackResult.envelope,
|
|
1037
|
+
ack: {
|
|
1038
|
+
event: 'reply.accepted',
|
|
1039
|
+
data: fallbackResult.body,
|
|
1040
|
+
},
|
|
1041
|
+
transport: 'http',
|
|
1042
|
+
fallbackUsed: true,
|
|
1043
|
+
};
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1046
|
+
if (isReplyAlreadyApplied(fallbackResult, fallbackResult.envelope.deliveryId)) {
|
|
1047
|
+
return {
|
|
1048
|
+
ok: true,
|
|
1049
|
+
envelope: fallbackResult.envelope,
|
|
1050
|
+
ack: {
|
|
1051
|
+
event: 'reply.accepted',
|
|
1052
|
+
data: {
|
|
1053
|
+
...(fallbackResult.body && typeof fallbackResult.body === 'object' ? fallbackResult.body : {}),
|
|
1054
|
+
repliedDeliveryId: fallbackResult.envelope.deliveryId,
|
|
1055
|
+
},
|
|
1056
|
+
},
|
|
1057
|
+
transport: 'http-already-applied',
|
|
1058
|
+
fallbackUsed: true,
|
|
1059
|
+
};
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
throw buildReplyFallbackError({
|
|
1063
|
+
deliveryId: fallbackResult.envelope.deliveryId,
|
|
1064
|
+
status: fallbackResult.status,
|
|
1065
|
+
body: fallbackResult.body,
|
|
1066
|
+
context: this.buildBoundaryContext({
|
|
1067
|
+
stage: 'reply_fallback',
|
|
1068
|
+
deliveryId: fallbackResult.envelope.deliveryId,
|
|
1069
|
+
sessionKey: normalizeOptionalText(sessionKey) || null,
|
|
1070
|
+
fallbackFrom: error?.code || error?.message || null,
|
|
1071
|
+
}),
|
|
1072
|
+
});
|
|
1073
|
+
}
|
|
1074
|
+
|
|
946
1075
|
async acceptDeliveryHttp({ deliveryId, sessionKey = null, source = 'runtime_dispatch' } = {}) {
|
|
947
1076
|
const normalizedDeliveryId = normalizeOptionalText(deliveryId);
|
|
948
1077
|
const result = await this.requestJsonWithDeliveryVisibilityRetry(`/v1/runtime-deliveries/${encodeURIComponent(normalizedDeliveryId)}/accepted`, {
|
|
@@ -976,16 +1105,43 @@ export class ClaworldRelayClient extends EventEmitter {
|
|
|
976
1105
|
timeoutMs = DEFAULT_REPLY_ACK_TIMEOUT_MS,
|
|
977
1106
|
httpFallback = true,
|
|
978
1107
|
} = {}) {
|
|
1108
|
+
if (httpFallback && (!this.ws || this.ws.readyState !== 1)) {
|
|
1109
|
+
return await this.submitReplyHttpFallback({
|
|
1110
|
+
deliveryId,
|
|
1111
|
+
sessionKey,
|
|
1112
|
+
replyText,
|
|
1113
|
+
source,
|
|
1114
|
+
error: this.buildWsNotConnectedError('reply_send'),
|
|
1115
|
+
});
|
|
1116
|
+
}
|
|
1117
|
+
|
|
1118
|
+
const ackAbortController = new AbortController();
|
|
979
1119
|
const ackPromise = this.waitForReplyAck({
|
|
980
1120
|
deliveryId,
|
|
981
1121
|
timeoutMs,
|
|
1122
|
+
signal: ackAbortController.signal,
|
|
982
1123
|
});
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
1124
|
+
let envelope;
|
|
1125
|
+
|
|
1126
|
+
try {
|
|
1127
|
+
envelope = this.sendReply({
|
|
1128
|
+
deliveryId,
|
|
1129
|
+
sessionKey,
|
|
1130
|
+
replyText,
|
|
1131
|
+
source,
|
|
1132
|
+
});
|
|
1133
|
+
} catch (error) {
|
|
1134
|
+
ackAbortController.abort();
|
|
1135
|
+
void ackPromise.catch(() => {});
|
|
1136
|
+
if (!httpFallback) throw error;
|
|
1137
|
+
return await this.submitReplyHttpFallback({
|
|
1138
|
+
deliveryId,
|
|
1139
|
+
sessionKey,
|
|
1140
|
+
replyText,
|
|
1141
|
+
source,
|
|
1142
|
+
error,
|
|
1143
|
+
});
|
|
1144
|
+
}
|
|
989
1145
|
|
|
990
1146
|
try {
|
|
991
1147
|
const ack = await ackPromise;
|
|
@@ -999,59 +1155,12 @@ export class ClaworldRelayClient extends EventEmitter {
|
|
|
999
1155
|
} catch (error) {
|
|
1000
1156
|
if (!httpFallback) throw error;
|
|
1001
1157
|
|
|
1002
|
-
this.
|
|
1003
|
-
accountId: this.runtimeConfig?.accountId || null,
|
|
1004
|
-
agentId: this.boundAgentId,
|
|
1158
|
+
return await this.submitReplyHttpFallback({
|
|
1005
1159
|
deliveryId: envelope.deliveryId,
|
|
1006
1160
|
sessionKey: envelope.sessionKey,
|
|
1007
|
-
error: error?.message || String(error),
|
|
1008
|
-
});
|
|
1009
|
-
|
|
1010
|
-
const fallbackResult = await this.replyToDeliveryHttp({
|
|
1011
|
-
deliveryId: envelope.deliveryId,
|
|
1012
1161
|
replyText,
|
|
1013
1162
|
source,
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
if (fallbackResult.status >= 200 && fallbackResult.status < 300) {
|
|
1017
|
-
return {
|
|
1018
|
-
ok: true,
|
|
1019
|
-
envelope,
|
|
1020
|
-
ack: {
|
|
1021
|
-
event: 'reply.accepted',
|
|
1022
|
-
data: fallbackResult.body,
|
|
1023
|
-
},
|
|
1024
|
-
transport: 'http',
|
|
1025
|
-
fallbackUsed: true,
|
|
1026
|
-
};
|
|
1027
|
-
}
|
|
1028
|
-
|
|
1029
|
-
if (isReplyAlreadyApplied(fallbackResult, envelope.deliveryId)) {
|
|
1030
|
-
return {
|
|
1031
|
-
ok: true,
|
|
1032
|
-
envelope,
|
|
1033
|
-
ack: {
|
|
1034
|
-
event: 'reply.accepted',
|
|
1035
|
-
data: {
|
|
1036
|
-
...(fallbackResult.body && typeof fallbackResult.body === 'object' ? fallbackResult.body : {}),
|
|
1037
|
-
repliedDeliveryId: envelope.deliveryId,
|
|
1038
|
-
},
|
|
1039
|
-
},
|
|
1040
|
-
transport: 'http-already-applied',
|
|
1041
|
-
fallbackUsed: true,
|
|
1042
|
-
};
|
|
1043
|
-
}
|
|
1044
|
-
|
|
1045
|
-
throw buildReplyFallbackError({
|
|
1046
|
-
deliveryId: envelope.deliveryId,
|
|
1047
|
-
status: fallbackResult.status,
|
|
1048
|
-
body: fallbackResult.body,
|
|
1049
|
-
context: this.buildBoundaryContext({
|
|
1050
|
-
stage: 'reply_fallback',
|
|
1051
|
-
deliveryId: envelope.deliveryId,
|
|
1052
|
-
sessionKey: envelope.sessionKey,
|
|
1053
|
-
fallbackFrom: error?.code || error?.message || null,
|
|
1054
|
-
}),
|
|
1163
|
+
error,
|
|
1055
1164
|
});
|
|
1056
1165
|
}
|
|
1057
1166
|
}
|
|
@@ -1169,6 +1278,69 @@ export class ClaworldRelayClient extends EventEmitter {
|
|
|
1169
1278
|
};
|
|
1170
1279
|
}
|
|
1171
1280
|
|
|
1281
|
+
async submitKeepSilentHttpFallback({
|
|
1282
|
+
deliveryId,
|
|
1283
|
+
sessionKey,
|
|
1284
|
+
reason = null,
|
|
1285
|
+
source = 'openclaw-autochain',
|
|
1286
|
+
error = null,
|
|
1287
|
+
} = {}) {
|
|
1288
|
+
this.logger.warn?.('[claworld:relay-client] kept_silent websocket transport failed; attempting HTTP fallback', {
|
|
1289
|
+
accountId: this.runtimeConfig?.accountId || null,
|
|
1290
|
+
agentId: this.boundAgentId,
|
|
1291
|
+
deliveryId: normalizeOptionalText(deliveryId),
|
|
1292
|
+
sessionKey: normalizeOptionalText(sessionKey) || null,
|
|
1293
|
+
error: error?.message || String(error),
|
|
1294
|
+
});
|
|
1295
|
+
|
|
1296
|
+
const fallbackResult = await this.keepDeliverySilentHttp({
|
|
1297
|
+
deliveryId,
|
|
1298
|
+
reason,
|
|
1299
|
+
source,
|
|
1300
|
+
});
|
|
1301
|
+
|
|
1302
|
+
if (fallbackResult.status >= 200 && fallbackResult.status < 300) {
|
|
1303
|
+
return {
|
|
1304
|
+
ok: true,
|
|
1305
|
+
envelope: fallbackResult.envelope,
|
|
1306
|
+
ack: {
|
|
1307
|
+
event: 'kept_silent.accepted',
|
|
1308
|
+
data: fallbackResult.body,
|
|
1309
|
+
},
|
|
1310
|
+
transport: 'http',
|
|
1311
|
+
fallbackUsed: true,
|
|
1312
|
+
};
|
|
1313
|
+
}
|
|
1314
|
+
|
|
1315
|
+
if (isDeliveryKeptSilentAlreadyApplied(fallbackResult, fallbackResult.envelope.deliveryId)) {
|
|
1316
|
+
return {
|
|
1317
|
+
ok: true,
|
|
1318
|
+
envelope: fallbackResult.envelope,
|
|
1319
|
+
ack: {
|
|
1320
|
+
event: 'kept_silent.accepted',
|
|
1321
|
+
data: {
|
|
1322
|
+
...(fallbackResult.body && typeof fallbackResult.body === 'object' ? fallbackResult.body : {}),
|
|
1323
|
+
keptSilentDeliveryId: fallbackResult.envelope.deliveryId,
|
|
1324
|
+
},
|
|
1325
|
+
},
|
|
1326
|
+
transport: 'http-already-applied',
|
|
1327
|
+
fallbackUsed: true,
|
|
1328
|
+
};
|
|
1329
|
+
}
|
|
1330
|
+
|
|
1331
|
+
throw buildKeepSilentFallbackError({
|
|
1332
|
+
deliveryId: fallbackResult.envelope.deliveryId,
|
|
1333
|
+
status: fallbackResult.status,
|
|
1334
|
+
body: fallbackResult.body,
|
|
1335
|
+
context: this.buildBoundaryContext({
|
|
1336
|
+
stage: 'kept_silent_fallback',
|
|
1337
|
+
deliveryId: fallbackResult.envelope.deliveryId,
|
|
1338
|
+
sessionKey: normalizeOptionalText(sessionKey) || null,
|
|
1339
|
+
fallbackFrom: error?.code || error?.message || null,
|
|
1340
|
+
}),
|
|
1341
|
+
});
|
|
1342
|
+
}
|
|
1343
|
+
|
|
1172
1344
|
async sendKeepSilentAndWaitForAck({
|
|
1173
1345
|
deliveryId,
|
|
1174
1346
|
sessionKey,
|
|
@@ -1177,16 +1349,43 @@ export class ClaworldRelayClient extends EventEmitter {
|
|
|
1177
1349
|
timeoutMs = DEFAULT_REPLY_ACK_TIMEOUT_MS,
|
|
1178
1350
|
httpFallback = true,
|
|
1179
1351
|
} = {}) {
|
|
1352
|
+
if (httpFallback && (!this.ws || this.ws.readyState !== 1)) {
|
|
1353
|
+
return await this.submitKeepSilentHttpFallback({
|
|
1354
|
+
deliveryId,
|
|
1355
|
+
sessionKey,
|
|
1356
|
+
reason,
|
|
1357
|
+
source,
|
|
1358
|
+
error: this.buildWsNotConnectedError('kept_silent_send'),
|
|
1359
|
+
});
|
|
1360
|
+
}
|
|
1361
|
+
|
|
1362
|
+
const ackAbortController = new AbortController();
|
|
1180
1363
|
const ackPromise = this.waitForKeepSilentAck({
|
|
1181
1364
|
deliveryId,
|
|
1182
1365
|
timeoutMs,
|
|
1366
|
+
signal: ackAbortController.signal,
|
|
1183
1367
|
});
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1368
|
+
let envelope;
|
|
1369
|
+
|
|
1370
|
+
try {
|
|
1371
|
+
envelope = this.sendKeepSilent({
|
|
1372
|
+
deliveryId,
|
|
1373
|
+
sessionKey,
|
|
1374
|
+
reason,
|
|
1375
|
+
source,
|
|
1376
|
+
});
|
|
1377
|
+
} catch (error) {
|
|
1378
|
+
ackAbortController.abort();
|
|
1379
|
+
void ackPromise.catch(() => {});
|
|
1380
|
+
if (!httpFallback) throw error;
|
|
1381
|
+
return await this.submitKeepSilentHttpFallback({
|
|
1382
|
+
deliveryId,
|
|
1383
|
+
sessionKey,
|
|
1384
|
+
reason,
|
|
1385
|
+
source,
|
|
1386
|
+
error,
|
|
1387
|
+
});
|
|
1388
|
+
}
|
|
1190
1389
|
|
|
1191
1390
|
try {
|
|
1192
1391
|
const ack = await ackPromise;
|
|
@@ -1200,59 +1399,12 @@ export class ClaworldRelayClient extends EventEmitter {
|
|
|
1200
1399
|
} catch (error) {
|
|
1201
1400
|
if (!httpFallback) throw error;
|
|
1202
1401
|
|
|
1203
|
-
this.
|
|
1204
|
-
accountId: this.runtimeConfig?.accountId || null,
|
|
1205
|
-
agentId: this.boundAgentId,
|
|
1402
|
+
return await this.submitKeepSilentHttpFallback({
|
|
1206
1403
|
deliveryId: envelope.deliveryId,
|
|
1207
1404
|
sessionKey: envelope.sessionKey,
|
|
1208
|
-
error: error?.message || String(error),
|
|
1209
|
-
});
|
|
1210
|
-
|
|
1211
|
-
const fallbackResult = await this.keepDeliverySilentHttp({
|
|
1212
|
-
deliveryId: envelope.deliveryId,
|
|
1213
1405
|
reason: envelope.reason,
|
|
1214
1406
|
source: envelope.source,
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
if (fallbackResult.status >= 200 && fallbackResult.status < 300) {
|
|
1218
|
-
return {
|
|
1219
|
-
ok: true,
|
|
1220
|
-
envelope,
|
|
1221
|
-
ack: {
|
|
1222
|
-
event: 'kept_silent.accepted',
|
|
1223
|
-
data: fallbackResult.body,
|
|
1224
|
-
},
|
|
1225
|
-
transport: 'http',
|
|
1226
|
-
fallbackUsed: true,
|
|
1227
|
-
};
|
|
1228
|
-
}
|
|
1229
|
-
|
|
1230
|
-
if (isDeliveryKeptSilentAlreadyApplied(fallbackResult, envelope.deliveryId)) {
|
|
1231
|
-
return {
|
|
1232
|
-
ok: true,
|
|
1233
|
-
envelope,
|
|
1234
|
-
ack: {
|
|
1235
|
-
event: 'kept_silent.accepted',
|
|
1236
|
-
data: {
|
|
1237
|
-
...(fallbackResult.body && typeof fallbackResult.body === 'object' ? fallbackResult.body : {}),
|
|
1238
|
-
keptSilentDeliveryId: envelope.deliveryId,
|
|
1239
|
-
},
|
|
1240
|
-
},
|
|
1241
|
-
transport: 'http-already-applied',
|
|
1242
|
-
fallbackUsed: true,
|
|
1243
|
-
};
|
|
1244
|
-
}
|
|
1245
|
-
|
|
1246
|
-
throw buildKeepSilentFallbackError({
|
|
1247
|
-
deliveryId: envelope.deliveryId,
|
|
1248
|
-
status: fallbackResult.status,
|
|
1249
|
-
body: fallbackResult.body,
|
|
1250
|
-
context: this.buildBoundaryContext({
|
|
1251
|
-
stage: 'kept_silent_fallback',
|
|
1252
|
-
deliveryId: envelope.deliveryId,
|
|
1253
|
-
sessionKey: envelope.sessionKey,
|
|
1254
|
-
fallbackFrom: error?.code || error?.message || null,
|
|
1255
|
-
}),
|
|
1407
|
+
error,
|
|
1256
1408
|
});
|
|
1257
1409
|
}
|
|
1258
1410
|
}
|
|
@@ -1,15 +1,6 @@
|
|
|
1
1
|
export const CLAWORLD_PLUGIN_BRIDGE_PROTOCOL = 'claworld.delivery_reply.v1';
|
|
2
2
|
|
|
3
3
|
const DELIVERY_EVENT_TYPE = 'delivery';
|
|
4
|
-
const MANAGEMENT_EVENT_TYPES = new Set([
|
|
5
|
-
'notification',
|
|
6
|
-
'domain_notification',
|
|
7
|
-
'management_wake',
|
|
8
|
-
'management_tick',
|
|
9
|
-
'conversation_lifecycle',
|
|
10
|
-
'platform_recommendation',
|
|
11
|
-
'ops_recommendation',
|
|
12
|
-
]);
|
|
13
4
|
|
|
14
5
|
function normalizeText(value, fallback = null) {
|
|
15
6
|
if (value == null) return fallback;
|
|
@@ -25,28 +16,27 @@ function normalizePayload(payload = null) {
|
|
|
25
16
|
export function createRelayEventProtocol() {
|
|
26
17
|
return {
|
|
27
18
|
version: CLAWORLD_PLUGIN_BRIDGE_PROTOCOL,
|
|
28
|
-
eventTypes: [DELIVERY_EVENT_TYPE
|
|
29
|
-
requiredEnvelopeFields: ['eventType', 'sessionKey', 'payload'],
|
|
19
|
+
eventTypes: [DELIVERY_EVENT_TYPE],
|
|
20
|
+
requiredEnvelopeFields: ['eventType', 'deliveryId', 'sessionKey', 'payload'],
|
|
30
21
|
describeEvent(event = {}) {
|
|
31
22
|
const payload = normalizePayload(event.payload);
|
|
32
23
|
const missing = [];
|
|
33
|
-
|
|
34
|
-
if (eventType !== DELIVERY_EVENT_TYPE && !MANAGEMENT_EVENT_TYPES.has(eventType)) {
|
|
24
|
+
if (normalizeText(event.eventType, null) !== DELIVERY_EVENT_TYPE) {
|
|
35
25
|
missing.push('eventType');
|
|
36
26
|
}
|
|
37
|
-
if (
|
|
27
|
+
if (!normalizeText(event.deliveryId, null)) {
|
|
38
28
|
missing.push('deliveryId');
|
|
39
29
|
}
|
|
40
30
|
if (!normalizeText(event.sessionKey, null)) {
|
|
41
31
|
missing.push('sessionKey');
|
|
42
32
|
}
|
|
43
|
-
if (
|
|
33
|
+
if (!normalizeText(payload.text, null)) {
|
|
44
34
|
missing.push('payload.text');
|
|
45
35
|
}
|
|
46
36
|
return {
|
|
47
37
|
ok: missing.length === 0,
|
|
48
38
|
missing,
|
|
49
|
-
role:
|
|
39
|
+
role: 'delivery',
|
|
50
40
|
};
|
|
51
41
|
},
|
|
52
42
|
};
|
|
@@ -47,6 +47,10 @@ export function buildConversationSessionKey(conversationKey = null, fallbackSess
|
|
|
47
47
|
export function resolveRuntimeSessionTarget(event = {}, options = {}) {
|
|
48
48
|
const payload = normalizePayload(event.payload);
|
|
49
49
|
const eventType = normalizeText(event.eventType || event.type || payload.eventType, null);
|
|
50
|
+
const providedSessionKind = normalizeText(
|
|
51
|
+
event.sessionKind,
|
|
52
|
+
normalizeText(payload.sessionKind, normalizeText(options.sessionKind, null)),
|
|
53
|
+
);
|
|
50
54
|
const targetAgentId = normalizeText(
|
|
51
55
|
event.targetAgentId,
|
|
52
56
|
normalizeText(payload.targetAgentId, normalizeText(options.targetAgentId, null)),
|
|
@@ -60,15 +64,15 @@ export function resolveRuntimeSessionTarget(event = {}, options = {}) {
|
|
|
60
64
|
normalizeText(payload.sessionKey, normalizeText(options.sessionKey, null)),
|
|
61
65
|
);
|
|
62
66
|
|
|
63
|
-
if (CLAWORLD_MANAGEMENT_EVENT_TYPES.includes(eventType)) {
|
|
67
|
+
if (providedSessionKind === CLAWORLD_SESSION_KINDS.management || CLAWORLD_MANAGEMENT_EVENT_TYPES.includes(eventType)) {
|
|
64
68
|
const managementSessionKey = normalizeText(
|
|
65
69
|
options.managementSessionKey,
|
|
66
|
-
buildManagementSessionKey(targetAgentId),
|
|
70
|
+
normalizeText(providedSessionKey, buildManagementSessionKey(targetAgentId)),
|
|
67
71
|
);
|
|
68
72
|
return {
|
|
69
73
|
sessionKind: CLAWORLD_SESSION_KINDS.management,
|
|
70
74
|
target: normalizeText(options.managementTarget, 'management_session'),
|
|
71
|
-
sessionKey:
|
|
75
|
+
sessionKey: providedSessionKey || managementSessionKey,
|
|
72
76
|
managementSessionKey: managementSessionKey || null,
|
|
73
77
|
conversationSessionKey: conversationKey ? buildConversationSessionKey(conversationKey) : null,
|
|
74
78
|
targetAgentId,
|