@xfxstudio/claworld 0.2.9 → 0.2.10-beta.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 +1 -1
- package/openclaw.plugin.json +7 -63
- package/package.json +6 -2
- package/skills/claworld-help/SKILL.md +5 -1
- package/skills/claworld-join-and-chat/SKILL.md +21 -1
- package/skills/claworld-manage-worlds/SKILL.md +81 -10
- package/src/lib/agent-profile.js +8 -3
- package/src/lib/chat-request.js +0 -1
- package/src/lib/policy.js +2 -6
- package/src/lib/public-identity.js +175 -0
- package/src/lib/relay/kickoff-text.js +1 -0
- package/src/openclaw/installer/cli.js +48 -4
- package/src/openclaw/installer/constants.js +1 -0
- package/src/openclaw/installer/core.js +247 -71
- package/src/openclaw/installer/doctor.js +31 -17
- package/src/openclaw/plugin/account-identity.js +1 -2
- package/src/openclaw/plugin/claworld-channel-plugin.js +453 -263
- package/src/openclaw/plugin/config-schema.js +9 -23
- package/src/openclaw/plugin/managed-config.js +294 -84
- package/src/openclaw/plugin/onboarding.js +37 -45
- package/src/openclaw/plugin/register.js +124 -13
- package/src/openclaw/plugin/relay-client.js +233 -17
- package/src/openclaw/runtime/backend-error-context.js +91 -0
- package/src/openclaw/runtime/feedback-helper.js +1 -2
- package/src/openclaw/runtime/product-shell-helper.js +43 -9
- package/src/openclaw/runtime/tool-contracts.js +26 -3
- package/src/openclaw/runtime/tool-inventory.js +7 -0
- package/src/openclaw/runtime/world-moderation-helper.js +3 -19
- package/src/product-shell/contracts/candidate-feed.js +7 -0
- package/src/product-shell/contracts/world-manifest.js +0 -1
- package/src/product-shell/contracts/world-orchestration.js +10 -1
- package/src/product-shell/conversation-feedback/conversation-feedback-service.js +261 -0
- package/src/product-shell/feedback/feedback-routes.js +0 -1
- package/src/product-shell/feedback/feedback-service.js +4 -9
- package/src/product-shell/index.js +40 -7
- package/src/product-shell/matching/matchmaking-service.js +22 -1
- package/src/product-shell/membership/membership-service.js +5 -1
- package/src/product-shell/onboarding/onboarding-service.js +16 -26
- package/src/product-shell/profile/public-identity-routes.js +60 -0
- package/src/product-shell/profile/public-identity-service.js +190 -0
- package/src/product-shell/search/search-service.js +9 -2
- package/src/product-shell/social/chat-request-service.js +22 -7
- package/src/product-shell/social/friend-routes.js +1 -1
- package/src/product-shell/social/friend-service.js +16 -19
- package/src/product-shell/social/social-routes.js +2 -2
- package/src/product-shell/social/social-service.js +31 -35
- package/src/product-shell/worlds/world-admin-service.js +31 -10
- package/src/product-shell/worlds/world-broadcast-service.js +2 -2
- package/src/lib/agent-address.js +0 -46
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { EventEmitter } from 'events';
|
|
2
2
|
import WebSocket from 'ws';
|
|
3
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
3
4
|
import { resolveClaworldRuntimeConfig } from './config-schema.js';
|
|
4
5
|
import { buildRuntimeAuthHeaders } from './account-identity.js';
|
|
5
6
|
import { createRelayEventProtocol } from '../protocol/relay-event-protocol.js';
|
|
@@ -43,6 +44,9 @@ function buildInboundEnvelope(message = {}) {
|
|
|
43
44
|
eventType: data.eventType || 'delivery',
|
|
44
45
|
deliveryId: data.deliveryId || null,
|
|
45
46
|
sessionKey: data.sessionKey || null,
|
|
47
|
+
createdAt: data.createdAt || null,
|
|
48
|
+
updatedAt: data.updatedAt || null,
|
|
49
|
+
turnCreatedAt: data.turnCreatedAt || null,
|
|
46
50
|
payload: data.payload && typeof data.payload === 'object' && !Array.isArray(data.payload)
|
|
47
51
|
? { ...data.payload }
|
|
48
52
|
: {},
|
|
@@ -56,6 +60,10 @@ function normalizeOptionalText(value) {
|
|
|
56
60
|
return normalized || null;
|
|
57
61
|
}
|
|
58
62
|
|
|
63
|
+
function resolveClientMessageId(value = null) {
|
|
64
|
+
return normalizeOptionalText(value) || `cmsg_${uuidv4()}`;
|
|
65
|
+
}
|
|
66
|
+
|
|
59
67
|
function buildReplyAckTimeoutError({ deliveryId, timeoutMs, context = {} } = {}) {
|
|
60
68
|
return createRuntimeBoundaryError({
|
|
61
69
|
code: 'relay_reply_ack_timeout',
|
|
@@ -68,6 +76,18 @@ function buildReplyAckTimeoutError({ deliveryId, timeoutMs, context = {} } = {})
|
|
|
68
76
|
});
|
|
69
77
|
}
|
|
70
78
|
|
|
79
|
+
function buildAcceptedAckTimeoutError({ deliveryId, timeoutMs, context = {} } = {}) {
|
|
80
|
+
return createRuntimeBoundaryError({
|
|
81
|
+
code: 'relay_delivery_accept_ack_timeout',
|
|
82
|
+
category: 'transport',
|
|
83
|
+
status: 504,
|
|
84
|
+
message: `timed out waiting for relay delivery acceptance acknowledgement for ${deliveryId || 'unknown-delivery'} after ${timeoutMs}ms`,
|
|
85
|
+
publicMessage: 'relay delivery acceptance acknowledgement timed out',
|
|
86
|
+
recoverable: true,
|
|
87
|
+
context,
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
|
|
71
91
|
function buildReplyFallbackError({
|
|
72
92
|
deliveryId,
|
|
73
93
|
status,
|
|
@@ -655,6 +675,34 @@ export class ClaworldRelayClient extends EventEmitter {
|
|
|
655
675
|
this.send({ type: 'heartbeat' });
|
|
656
676
|
}
|
|
657
677
|
|
|
678
|
+
sendAccepted({ deliveryId, sessionKey, source = 'runtime_dispatch' } = {}) {
|
|
679
|
+
const normalizedDeliveryId = normalizeOptionalText(deliveryId);
|
|
680
|
+
if (!normalizedDeliveryId) {
|
|
681
|
+
throw createRuntimeBoundaryError({
|
|
682
|
+
code: 'relay_delivery_id_required',
|
|
683
|
+
category: 'input',
|
|
684
|
+
status: 400,
|
|
685
|
+
message: 'deliveryId is required to acknowledge relay delivery acceptance',
|
|
686
|
+
publicMessage: 'deliveryId is required',
|
|
687
|
+
recoverable: true,
|
|
688
|
+
});
|
|
689
|
+
}
|
|
690
|
+
const envelope = {
|
|
691
|
+
deliveryId: normalizedDeliveryId,
|
|
692
|
+
sessionKey: normalizeOptionalText(sessionKey) || null,
|
|
693
|
+
source: normalizeOptionalText(source) || 'runtime_dispatch',
|
|
694
|
+
};
|
|
695
|
+
this.send({
|
|
696
|
+
type: 'accepted',
|
|
697
|
+
deliveryId: envelope.deliveryId,
|
|
698
|
+
sessionKey: envelope.sessionKey,
|
|
699
|
+
payload: {
|
|
700
|
+
source: envelope.source,
|
|
701
|
+
},
|
|
702
|
+
});
|
|
703
|
+
return envelope;
|
|
704
|
+
}
|
|
705
|
+
|
|
658
706
|
sendReply({ deliveryId, sessionKey, replyText, source = 'subagent' } = {}) {
|
|
659
707
|
const envelope = this.outbound.createReplyEnvelope({
|
|
660
708
|
deliveryId,
|
|
@@ -791,6 +839,85 @@ export class ClaworldRelayClient extends EventEmitter {
|
|
|
791
839
|
});
|
|
792
840
|
}
|
|
793
841
|
|
|
842
|
+
waitForAcceptedAck({ deliveryId, timeoutMs = DEFAULT_REPLY_ACK_TIMEOUT_MS } = {}) {
|
|
843
|
+
const normalizedDeliveryId = normalizeOptionalText(deliveryId);
|
|
844
|
+
if (!normalizedDeliveryId) {
|
|
845
|
+
return Promise.reject(createRuntimeBoundaryError({
|
|
846
|
+
code: 'relay_delivery_id_required',
|
|
847
|
+
category: 'input',
|
|
848
|
+
status: 400,
|
|
849
|
+
message: 'deliveryId is required to wait for relay delivery acceptance acknowledgement',
|
|
850
|
+
publicMessage: 'deliveryId is required',
|
|
851
|
+
recoverable: true,
|
|
852
|
+
}));
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
return new Promise((resolve, reject) => {
|
|
856
|
+
let settled = false;
|
|
857
|
+
let timeout = null;
|
|
858
|
+
|
|
859
|
+
const cleanup = () => {
|
|
860
|
+
if (timeout) clearTimeout(timeout);
|
|
861
|
+
this.off('delivery.accepted', onAccepted);
|
|
862
|
+
this.off('disconnect', onDisconnect);
|
|
863
|
+
this.off('close', onDisconnect);
|
|
864
|
+
};
|
|
865
|
+
|
|
866
|
+
const settleResolve = (value) => {
|
|
867
|
+
if (settled) return;
|
|
868
|
+
settled = true;
|
|
869
|
+
cleanup();
|
|
870
|
+
resolve(value);
|
|
871
|
+
};
|
|
872
|
+
|
|
873
|
+
const settleReject = (error) => {
|
|
874
|
+
if (settled) return;
|
|
875
|
+
settled = true;
|
|
876
|
+
cleanup();
|
|
877
|
+
reject(error);
|
|
878
|
+
};
|
|
879
|
+
|
|
880
|
+
const onAccepted = (message = {}) => {
|
|
881
|
+
const acceptedDeliveryId = normalizeOptionalText(message?.data?.acceptedDeliveryId);
|
|
882
|
+
if (acceptedDeliveryId !== normalizedDeliveryId) return;
|
|
883
|
+
settleResolve(message);
|
|
884
|
+
};
|
|
885
|
+
|
|
886
|
+
const onDisconnect = (info = {}) => {
|
|
887
|
+
settleReject(createRuntimeBoundaryError({
|
|
888
|
+
code: 'relay_delivery_accept_ack_disconnected',
|
|
889
|
+
category: 'transport',
|
|
890
|
+
status: 502,
|
|
891
|
+
message: `relay websocket closed before delivery acceptance acknowledgement for ${normalizedDeliveryId}`,
|
|
892
|
+
publicMessage: 'relay websocket closed before delivery acceptance acknowledgement',
|
|
893
|
+
recoverable: true,
|
|
894
|
+
context: this.buildBoundaryContext({
|
|
895
|
+
stage: 'delivery_accept_ack_wait',
|
|
896
|
+
deliveryId: normalizedDeliveryId,
|
|
897
|
+
closeCode: info?.code ?? null,
|
|
898
|
+
closeReason: info?.reason || null,
|
|
899
|
+
}),
|
|
900
|
+
}));
|
|
901
|
+
};
|
|
902
|
+
|
|
903
|
+
this.on('delivery.accepted', onAccepted);
|
|
904
|
+
this.on('disconnect', onDisconnect);
|
|
905
|
+
this.on('close', onDisconnect);
|
|
906
|
+
|
|
907
|
+
timeout = setTimeout(() => {
|
|
908
|
+
settleReject(buildAcceptedAckTimeoutError({
|
|
909
|
+
deliveryId: normalizedDeliveryId,
|
|
910
|
+
timeoutMs,
|
|
911
|
+
context: this.buildBoundaryContext({
|
|
912
|
+
stage: 'delivery_accept_ack_wait',
|
|
913
|
+
deliveryId: normalizedDeliveryId,
|
|
914
|
+
}),
|
|
915
|
+
}));
|
|
916
|
+
}, timeoutMs);
|
|
917
|
+
if (typeof timeout.unref === 'function') timeout.unref();
|
|
918
|
+
});
|
|
919
|
+
}
|
|
920
|
+
|
|
794
921
|
waitForKeepSilentAck({ deliveryId, timeoutMs = DEFAULT_REPLY_ACK_TIMEOUT_MS } = {}) {
|
|
795
922
|
const normalizedDeliveryId = normalizeOptionalText(deliveryId);
|
|
796
923
|
if (!normalizedDeliveryId) {
|
|
@@ -897,6 +1024,31 @@ export class ClaworldRelayClient extends EventEmitter {
|
|
|
897
1024
|
};
|
|
898
1025
|
}
|
|
899
1026
|
|
|
1027
|
+
async acceptDeliveryHttp({ deliveryId, sessionKey = null, source = 'runtime_dispatch' } = {}) {
|
|
1028
|
+
const normalizedDeliveryId = normalizeOptionalText(deliveryId);
|
|
1029
|
+
const result = await this.requestJson(`/v1/deliveries/${encodeURIComponent(normalizedDeliveryId)}/accepted`, {
|
|
1030
|
+
method: 'POST',
|
|
1031
|
+
headers: buildRuntimeAuthHeaders(this.runtimeConfig, { 'content-type': 'application/json' }),
|
|
1032
|
+
body: JSON.stringify({
|
|
1033
|
+
fromAgentId: this.boundAgentId,
|
|
1034
|
+
sessionKey: normalizeOptionalText(sessionKey) || null,
|
|
1035
|
+
source: normalizeOptionalText(source) || 'runtime_dispatch',
|
|
1036
|
+
}),
|
|
1037
|
+
}, {
|
|
1038
|
+
code: 'relay_delivery_accept_fallback_failed',
|
|
1039
|
+
message: 'failed to submit relay delivery acceptance fallback',
|
|
1040
|
+
publicMessage: 'failed to submit relay delivery acceptance fallback',
|
|
1041
|
+
});
|
|
1042
|
+
return {
|
|
1043
|
+
...result,
|
|
1044
|
+
envelope: {
|
|
1045
|
+
deliveryId: normalizedDeliveryId,
|
|
1046
|
+
sessionKey: normalizeOptionalText(sessionKey) || null,
|
|
1047
|
+
source: normalizeOptionalText(source) || 'runtime_dispatch',
|
|
1048
|
+
},
|
|
1049
|
+
};
|
|
1050
|
+
}
|
|
1051
|
+
|
|
900
1052
|
async sendReplyAndWaitForAck({
|
|
901
1053
|
deliveryId,
|
|
902
1054
|
sessionKey,
|
|
@@ -984,6 +1136,75 @@ export class ClaworldRelayClient extends EventEmitter {
|
|
|
984
1136
|
}
|
|
985
1137
|
}
|
|
986
1138
|
|
|
1139
|
+
async sendAcceptedAndWaitForAck({
|
|
1140
|
+
deliveryId,
|
|
1141
|
+
sessionKey,
|
|
1142
|
+
source = 'runtime_dispatch',
|
|
1143
|
+
timeoutMs = DEFAULT_REPLY_ACK_TIMEOUT_MS,
|
|
1144
|
+
httpFallback = true,
|
|
1145
|
+
} = {}) {
|
|
1146
|
+
const envelope = this.sendAccepted({
|
|
1147
|
+
deliveryId,
|
|
1148
|
+
sessionKey,
|
|
1149
|
+
source,
|
|
1150
|
+
});
|
|
1151
|
+
|
|
1152
|
+
try {
|
|
1153
|
+
const ack = await this.waitForAcceptedAck({
|
|
1154
|
+
deliveryId: envelope.deliveryId,
|
|
1155
|
+
timeoutMs,
|
|
1156
|
+
});
|
|
1157
|
+
return {
|
|
1158
|
+
ok: true,
|
|
1159
|
+
envelope,
|
|
1160
|
+
ack,
|
|
1161
|
+
transport: 'websocket',
|
|
1162
|
+
fallbackUsed: false,
|
|
1163
|
+
};
|
|
1164
|
+
} catch (error) {
|
|
1165
|
+
if (!httpFallback) throw error;
|
|
1166
|
+
|
|
1167
|
+
this.logger.warn?.('[claworld:relay-client] delivery acceptance websocket acknowledgement failed; attempting HTTP fallback', {
|
|
1168
|
+
accountId: this.runtimeConfig?.accountId || null,
|
|
1169
|
+
agentId: this.boundAgentId,
|
|
1170
|
+
deliveryId: envelope.deliveryId,
|
|
1171
|
+
sessionKey: envelope.sessionKey,
|
|
1172
|
+
error: error?.message || String(error),
|
|
1173
|
+
});
|
|
1174
|
+
|
|
1175
|
+
const fallbackResult = await this.acceptDeliveryHttp({
|
|
1176
|
+
deliveryId: envelope.deliveryId,
|
|
1177
|
+
sessionKey: envelope.sessionKey,
|
|
1178
|
+
source: envelope.source,
|
|
1179
|
+
});
|
|
1180
|
+
|
|
1181
|
+
if (fallbackResult.status >= 200 && fallbackResult.status < 300) {
|
|
1182
|
+
return {
|
|
1183
|
+
ok: true,
|
|
1184
|
+
envelope,
|
|
1185
|
+
ack: {
|
|
1186
|
+
event: 'delivery.accepted',
|
|
1187
|
+
data: fallbackResult.body,
|
|
1188
|
+
},
|
|
1189
|
+
transport: 'http',
|
|
1190
|
+
fallbackUsed: true,
|
|
1191
|
+
};
|
|
1192
|
+
}
|
|
1193
|
+
|
|
1194
|
+
throw buildReplyFallbackError({
|
|
1195
|
+
deliveryId: envelope.deliveryId,
|
|
1196
|
+
status: fallbackResult.status,
|
|
1197
|
+
body: fallbackResult.body,
|
|
1198
|
+
context: this.buildBoundaryContext({
|
|
1199
|
+
stage: 'delivery_accept_fallback',
|
|
1200
|
+
deliveryId: envelope.deliveryId,
|
|
1201
|
+
sessionKey: envelope.sessionKey,
|
|
1202
|
+
fallbackFrom: error?.code || error?.message || null,
|
|
1203
|
+
}),
|
|
1204
|
+
});
|
|
1205
|
+
}
|
|
1206
|
+
}
|
|
1207
|
+
|
|
987
1208
|
async keepDeliverySilentHttp({ deliveryId, reason = null, source = 'openclaw-autochain' } = {}) {
|
|
988
1209
|
const normalizedDeliveryId = normalizeOptionalText(deliveryId);
|
|
989
1210
|
const result = await this.requestJson(`/v1/deliveries/${encodeURIComponent(normalizedDeliveryId)}/kept-silent`, {
|
|
@@ -1096,24 +1317,14 @@ export class ClaworldRelayClient extends EventEmitter {
|
|
|
1096
1317
|
}
|
|
1097
1318
|
}
|
|
1098
1319
|
|
|
1099
|
-
async createChatRequest({ fromAgentId,
|
|
1100
|
-
const target = await this.requestJson(`/v1/agents/resolve?address=${encodeURIComponent(toAddress)}`, {
|
|
1101
|
-
method: 'GET',
|
|
1102
|
-
headers: buildRuntimeAuthHeaders(this.runtimeConfig),
|
|
1103
|
-
}, {
|
|
1104
|
-
code: 'relay_target_resolve_failed',
|
|
1105
|
-
message: 'failed to resolve relay target',
|
|
1106
|
-
publicMessage: 'failed to resolve relay target',
|
|
1107
|
-
});
|
|
1108
|
-
if (target.status >= 400) return target;
|
|
1109
|
-
|
|
1320
|
+
async createChatRequest({ fromAgentId, targetAgentId, requestContext = {} } = {}) {
|
|
1110
1321
|
const normalized = normalizeChatRequestInput({ requestContext, source: 'direct_lookup' });
|
|
1111
1322
|
return await this.requestJson('/v1/chat-requests', {
|
|
1112
1323
|
method: 'POST',
|
|
1113
1324
|
headers: buildRuntimeAuthHeaders(this.runtimeConfig, { 'content-type': 'application/json' }),
|
|
1114
1325
|
body: JSON.stringify({
|
|
1115
1326
|
fromAgentId,
|
|
1116
|
-
targetAgentId
|
|
1327
|
+
targetAgentId,
|
|
1117
1328
|
kickoffBrief: normalized.kickoffBrief || null,
|
|
1118
1329
|
openingMessage: normalized.openingMessage || null,
|
|
1119
1330
|
worldId: normalized.conversation?.worldId || null,
|
|
@@ -1164,16 +1375,21 @@ export class ClaworldRelayClient extends EventEmitter {
|
|
|
1164
1375
|
});
|
|
1165
1376
|
}
|
|
1166
1377
|
|
|
1167
|
-
async deliverMessage({ fromAgentId,
|
|
1168
|
-
|
|
1378
|
+
async deliverMessage({ fromAgentId, targetAgentId, clientMessageId = null, payload = {}, conversation = {} } = {}) {
|
|
1379
|
+
const resolvedClientMessageId = resolveClientMessageId(clientMessageId);
|
|
1380
|
+
const result = await this.requestJson('/v1/messages', {
|
|
1169
1381
|
method: 'POST',
|
|
1170
1382
|
headers: buildRuntimeAuthHeaders(this.runtimeConfig, { 'content-type': 'application/json' }),
|
|
1171
|
-
body: JSON.stringify({ fromAgentId,
|
|
1383
|
+
body: JSON.stringify({ fromAgentId, targetAgentId, clientMessageId: resolvedClientMessageId, payload, conversation }),
|
|
1172
1384
|
}, {
|
|
1173
1385
|
code: 'relay_message_delivery_failed',
|
|
1174
1386
|
message: 'failed to deliver relay message',
|
|
1175
1387
|
publicMessage: 'failed to deliver relay message',
|
|
1176
1388
|
});
|
|
1389
|
+
return {
|
|
1390
|
+
...result,
|
|
1391
|
+
clientMessageId: resolvedClientMessageId,
|
|
1392
|
+
};
|
|
1177
1393
|
}
|
|
1178
1394
|
|
|
1179
1395
|
waitFor(eventNameOrPredicate, timeoutMs = 8000) {
|
|
@@ -1201,7 +1417,7 @@ export class ClaworldRelayClient extends EventEmitter {
|
|
|
1201
1417
|
});
|
|
1202
1418
|
}
|
|
1203
1419
|
|
|
1204
|
-
async establishConversation({ fromAgentId,
|
|
1420
|
+
async establishConversation({ fromAgentId, targetAgentId, requestContext = {}, openingPayload = {} } = {}) {
|
|
1205
1421
|
const normalizedRequestContext = requestContext && typeof requestContext === 'object' && !Array.isArray(requestContext)
|
|
1206
1422
|
? { ...requestContext }
|
|
1207
1423
|
: {};
|
|
@@ -1217,7 +1433,7 @@ export class ClaworldRelayClient extends EventEmitter {
|
|
|
1217
1433
|
|
|
1218
1434
|
const requestResult = await this.createChatRequest({
|
|
1219
1435
|
fromAgentId,
|
|
1220
|
-
|
|
1436
|
+
targetAgentId,
|
|
1221
1437
|
requestContext: normalizedRequestContext,
|
|
1222
1438
|
});
|
|
1223
1439
|
if (requestResult.status !== 201) {
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
function normalizeText(value, fallback = null) {
|
|
2
|
+
if (value == null) return fallback;
|
|
3
|
+
const normalized = String(value).trim();
|
|
4
|
+
return normalized || fallback;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
function normalizeFieldError(fieldError = {}) {
|
|
8
|
+
const fieldId = normalizeText(fieldError.fieldId, null);
|
|
9
|
+
const message = normalizeText(fieldError.message, null);
|
|
10
|
+
const code = normalizeText(fieldError.code, null);
|
|
11
|
+
if (!fieldId && !message && !code) return null;
|
|
12
|
+
return {
|
|
13
|
+
...(fieldId ? { fieldId } : {}),
|
|
14
|
+
...(message ? { message } : {}),
|
|
15
|
+
...(code ? { code } : {}),
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function normalizeMissingField(field = {}) {
|
|
20
|
+
const fieldId = normalizeText(field.fieldId, null);
|
|
21
|
+
const label = normalizeText(field.label, null);
|
|
22
|
+
const description = normalizeText(field.description, null);
|
|
23
|
+
const message = normalizeText(field.message, null);
|
|
24
|
+
const code = normalizeText(field.code, null);
|
|
25
|
+
if (!fieldId && !label && !description && !message && !code) return null;
|
|
26
|
+
return {
|
|
27
|
+
...(fieldId ? { fieldId } : {}),
|
|
28
|
+
...(label ? { label } : {}),
|
|
29
|
+
...(description ? { description } : {}),
|
|
30
|
+
...(message ? { message } : {}),
|
|
31
|
+
...(code ? { code } : {}),
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function normalizePublicIdentity(publicIdentity = {}) {
|
|
36
|
+
const status = normalizeText(publicIdentity.status, null);
|
|
37
|
+
const displayName = normalizeText(publicIdentity.displayName, null);
|
|
38
|
+
const code = normalizeText(publicIdentity.code, null);
|
|
39
|
+
const displayIdentity = normalizeText(publicIdentity.displayIdentity, null);
|
|
40
|
+
const confirmedAt = normalizeText(publicIdentity.confirmedAt, null);
|
|
41
|
+
const updatedAt = normalizeText(publicIdentity.updatedAt, null);
|
|
42
|
+
if (!status && !displayName && !code && !displayIdentity && !confirmedAt && !updatedAt) return null;
|
|
43
|
+
return {
|
|
44
|
+
...(status ? { status } : {}),
|
|
45
|
+
...(displayName ? { displayName } : {}),
|
|
46
|
+
...(code ? { code } : {}),
|
|
47
|
+
...(displayIdentity ? { displayIdentity } : {}),
|
|
48
|
+
...(confirmedAt ? { confirmedAt } : {}),
|
|
49
|
+
...(updatedAt ? { updatedAt } : {}),
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function normalizeBackendFieldError(fieldError = {}) {
|
|
54
|
+
return normalizeFieldError(fieldError);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function normalizeBackendMissingField(field = {}) {
|
|
58
|
+
return normalizeMissingField(field);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function normalizeBackendPublicIdentity(publicIdentity = {}) {
|
|
62
|
+
return normalizePublicIdentity(publicIdentity);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function extractBackendErrorContext(payload = {}) {
|
|
66
|
+
if (!payload || typeof payload !== 'object' || Array.isArray(payload)) return {};
|
|
67
|
+
|
|
68
|
+
const backendCode = normalizeText(payload.error, null);
|
|
69
|
+
const backendMessage = normalizeText(payload.message, null);
|
|
70
|
+
const requiredAction = normalizeText(payload.requiredAction, null);
|
|
71
|
+
const nextAction = normalizeText(payload.nextAction, null);
|
|
72
|
+
const nextTool = normalizeText(payload.nextTool, null);
|
|
73
|
+
const fieldErrors = Array.isArray(payload.fieldErrors)
|
|
74
|
+
? payload.fieldErrors.map((fieldError) => normalizeFieldError(fieldError)).filter(Boolean)
|
|
75
|
+
: [];
|
|
76
|
+
const missingFields = Array.isArray(payload.missingFields)
|
|
77
|
+
? payload.missingFields.map((field) => normalizeMissingField(field)).filter(Boolean)
|
|
78
|
+
: [];
|
|
79
|
+
const publicIdentity = normalizePublicIdentity(payload.publicIdentity);
|
|
80
|
+
|
|
81
|
+
return {
|
|
82
|
+
...(backendCode ? { backendCode } : {}),
|
|
83
|
+
...(backendMessage ? { backendMessage } : {}),
|
|
84
|
+
...(requiredAction ? { requiredAction } : {}),
|
|
85
|
+
...(nextAction ? { nextAction } : {}),
|
|
86
|
+
...(nextTool ? { nextTool } : {}),
|
|
87
|
+
...(fieldErrors.length > 0 ? { fieldErrors } : {}),
|
|
88
|
+
...(missingFields.length > 0 ? { missingFields } : {}),
|
|
89
|
+
...(publicIdentity ? { publicIdentity } : {}),
|
|
90
|
+
};
|
|
91
|
+
}
|
|
@@ -114,7 +114,6 @@ export async function submitFeedbackReport({
|
|
|
114
114
|
turnId: normalizeText(normalizedContext.turnId, null),
|
|
115
115
|
deliveryId: normalizeText(normalizedContext.deliveryId, null),
|
|
116
116
|
targetAgentId: normalizeText(normalizedContext.targetAgentId, null),
|
|
117
|
-
targetAgentCode: normalizeText(normalizedContext.targetAgentCode, null),
|
|
118
117
|
tags: normalizeStringList(normalizedContext.tags),
|
|
119
118
|
metadata: normalizeObject(normalizedContext.metadata),
|
|
120
119
|
},
|
|
@@ -128,7 +127,7 @@ export async function submitFeedbackReport({
|
|
|
128
127
|
accountId: normalizeText(resolvedRuntimeConfig.accountId, normalizeText(accountId, null)),
|
|
129
128
|
serverUrl: baseUrl,
|
|
130
129
|
relayAgentId: resolvedAgentId,
|
|
131
|
-
|
|
130
|
+
defaultTargetAgentId: normalizeText(resolvedRuntimeConfig.relay?.defaultTargetAgentId, null),
|
|
132
131
|
},
|
|
133
132
|
}),
|
|
134
133
|
});
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { resolveClaworldRuntimeConfig } from '../plugin/config-schema.js';
|
|
2
2
|
import { buildRuntimeAuthHeaders } from '../plugin/account-identity.js';
|
|
3
3
|
import { createRuntimeBoundaryError } from '../../lib/runtime-errors.js';
|
|
4
|
+
import { extractBackendErrorContext } from './backend-error-context.js';
|
|
4
5
|
import {
|
|
5
6
|
buildCandidateDeliverySummary as buildBackendCandidateDeliverySummary,
|
|
6
7
|
buildWorldSelectionPrompt as buildBackendWorldSelectionPrompt,
|
|
@@ -337,6 +338,15 @@ function normalizeCandidate(candidate = {}, index = 0) {
|
|
|
337
338
|
? candidate.compatibilitySignals.map((signal, signalIndex) => normalizeCompatibilitySignal(signal, signalIndex))
|
|
338
339
|
: [],
|
|
339
340
|
deliveryReason: normalizeDeliveryReason(candidate.deliveryReason),
|
|
341
|
+
worldFeedbackSummary: candidate.worldFeedbackSummary && typeof candidate.worldFeedbackSummary === 'object'
|
|
342
|
+
? {
|
|
343
|
+
likesReceived: normalizeInteger(candidate.worldFeedbackSummary.likesReceived, 0),
|
|
344
|
+
dislikesReceived: normalizeInteger(candidate.worldFeedbackSummary.dislikesReceived, 0),
|
|
345
|
+
}
|
|
346
|
+
: {
|
|
347
|
+
likesReceived: 0,
|
|
348
|
+
dislikesReceived: 0,
|
|
349
|
+
},
|
|
340
350
|
expiresAt: normalizeText(candidate.expiresAt, null),
|
|
341
351
|
joinedAt: normalizeText(candidate.joinedAt, null),
|
|
342
352
|
rank: normalizedRank == null ? null : Math.max(1, Math.trunc(normalizedRank)),
|
|
@@ -437,7 +447,6 @@ function normalizeWorldSearchItem(item = {}, index = 0) {
|
|
|
437
447
|
worldId: normalizeText(item.worldId, 'unknown-world'),
|
|
438
448
|
displayName: normalizeText(item.displayName, `Player ${index + 1}`),
|
|
439
449
|
headline: normalizeText(item.headline, null),
|
|
440
|
-
address: normalizeText(item.address, null),
|
|
441
450
|
online: item.online === true,
|
|
442
451
|
connectedAt: normalizeText(item.connectedAt, null),
|
|
443
452
|
lastHeartbeatAt: normalizeText(item.lastHeartbeatAt, null),
|
|
@@ -450,6 +459,15 @@ function normalizeWorldSearchItem(item = {}, index = 0) {
|
|
|
450
459
|
reasonSummary: normalizeText(item.reasonSummary, ''),
|
|
451
460
|
joinedAt: normalizeText(item.joinedAt, null),
|
|
452
461
|
profileSummary: normalizeCandidateProfileSummary(item.profileSummary),
|
|
462
|
+
worldFeedbackSummary: item.worldFeedbackSummary && typeof item.worldFeedbackSummary === 'object'
|
|
463
|
+
? {
|
|
464
|
+
likesReceived: normalizeInteger(item.worldFeedbackSummary.likesReceived, 0),
|
|
465
|
+
dislikesReceived: normalizeInteger(item.worldFeedbackSummary.dislikesReceived, 0),
|
|
466
|
+
}
|
|
467
|
+
: {
|
|
468
|
+
likesReceived: 0,
|
|
469
|
+
dislikesReceived: 0,
|
|
470
|
+
},
|
|
453
471
|
};
|
|
454
472
|
}
|
|
455
473
|
|
|
@@ -686,8 +704,7 @@ function createProductShellHttpError(action, response, { accountId = null, world
|
|
|
686
704
|
accountId,
|
|
687
705
|
...(worldId ? { worldId } : {}),
|
|
688
706
|
httpStatus: response?.status ?? 500,
|
|
689
|
-
|
|
690
|
-
backendMessage,
|
|
707
|
+
...extractBackendErrorContext(response?.body),
|
|
691
708
|
},
|
|
692
709
|
});
|
|
693
710
|
}
|
|
@@ -776,7 +793,10 @@ export async function fetchWorldDetail({
|
|
|
776
793
|
accountId: resolvedRuntimeConfig.accountId || accountId || null,
|
|
777
794
|
body: detail.body,
|
|
778
795
|
});
|
|
779
|
-
throw
|
|
796
|
+
throw createProductShellHttpError('world_detail', detail, {
|
|
797
|
+
accountId: resolvedRuntimeConfig.accountId || accountId || null,
|
|
798
|
+
worldId: resolvedWorldId,
|
|
799
|
+
});
|
|
780
800
|
}
|
|
781
801
|
|
|
782
802
|
return normalizeWorldDetail(detail.body);
|
|
@@ -830,7 +850,10 @@ export async function joinWorld({
|
|
|
830
850
|
accountId: resolvedRuntimeConfig.accountId || accountId || null,
|
|
831
851
|
body: joinResult.body,
|
|
832
852
|
});
|
|
833
|
-
throw
|
|
853
|
+
throw createProductShellHttpError('world_join', joinResult, {
|
|
854
|
+
accountId: resolvedRuntimeConfig.accountId || accountId || null,
|
|
855
|
+
worldId: resolvedWorldId,
|
|
856
|
+
});
|
|
834
857
|
}
|
|
835
858
|
|
|
836
859
|
return normalizeWorldJoinResponse(joinResult.body, {
|
|
@@ -890,7 +913,10 @@ export async function fetchWorldSearch({
|
|
|
890
913
|
accountId: resolvedRuntimeConfig.accountId || accountId || null,
|
|
891
914
|
body: searchResult.body,
|
|
892
915
|
});
|
|
893
|
-
throw
|
|
916
|
+
throw createProductShellHttpError('world_search', searchResult, {
|
|
917
|
+
accountId: resolvedRuntimeConfig.accountId || accountId || null,
|
|
918
|
+
worldId: resolvedWorldId,
|
|
919
|
+
});
|
|
894
920
|
}
|
|
895
921
|
|
|
896
922
|
return normalizeWorldSearchResponse(searchResult.body, {
|
|
@@ -950,7 +976,10 @@ export async function submitWorldSearch({
|
|
|
950
976
|
accountId: resolvedRuntimeConfig.accountId || accountId || null,
|
|
951
977
|
body: searchResult.body,
|
|
952
978
|
});
|
|
953
|
-
throw
|
|
979
|
+
throw createProductShellHttpError('world_search', searchResult, {
|
|
980
|
+
accountId: resolvedRuntimeConfig.accountId || accountId || null,
|
|
981
|
+
worldId: resolvedWorldId,
|
|
982
|
+
});
|
|
954
983
|
}
|
|
955
984
|
|
|
956
985
|
if (searchResult.status === 409) {
|
|
@@ -1095,7 +1124,10 @@ export async function fetchWorldCandidateFeed({
|
|
|
1095
1124
|
accountId: resolvedRuntimeConfig.accountId || accountId || null,
|
|
1096
1125
|
body: candidateFeed.body,
|
|
1097
1126
|
});
|
|
1098
|
-
throw
|
|
1127
|
+
throw createProductShellHttpError('world_candidate_feed', candidateFeed, {
|
|
1128
|
+
accountId: resolvedRuntimeConfig.accountId || accountId || null,
|
|
1129
|
+
worldId: resolvedWorldId,
|
|
1130
|
+
});
|
|
1099
1131
|
}
|
|
1100
1132
|
|
|
1101
1133
|
return normalizeCandidateFeedResponse(candidateFeed.body, {
|
|
@@ -1136,7 +1168,9 @@ export async function resolveWorldSelectionFlow({
|
|
|
1136
1168
|
accountId: resolvedRuntimeConfig.accountId || accountId || null,
|
|
1137
1169
|
body: worlds.body,
|
|
1138
1170
|
});
|
|
1139
|
-
throw
|
|
1171
|
+
throw createProductShellHttpError('world_directory', worlds, {
|
|
1172
|
+
accountId: resolvedRuntimeConfig.accountId || accountId || null,
|
|
1173
|
+
});
|
|
1140
1174
|
}
|
|
1141
1175
|
|
|
1142
1176
|
return buildPostSetupWorldDirectory(worlds.body, {
|
|
@@ -85,6 +85,26 @@ function projectWorldStats(stats = null) {
|
|
|
85
85
|
totalParticipants: normalizeOptionalInteger(stats.totalParticipants, null),
|
|
86
86
|
activeParticipants: normalizeOptionalInteger(stats.activeParticipants, null),
|
|
87
87
|
totalConversationCount: normalizeOptionalInteger(stats.totalConversationCount, null),
|
|
88
|
+
totalLikes: normalizeOptionalInteger(stats.totalLikes, null),
|
|
89
|
+
totalDislikes: normalizeOptionalInteger(stats.totalDislikes, null),
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function projectWorldFeedbackSummary(summary = null) {
|
|
94
|
+
if (!summary || typeof summary !== 'object' || Array.isArray(summary)) return null;
|
|
95
|
+
return {
|
|
96
|
+
likesReceived: normalizeInteger(summary.likesReceived, 0),
|
|
97
|
+
dislikesReceived: normalizeInteger(summary.dislikesReceived, 0),
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function projectConversationFeedbackSummary(summary = null) {
|
|
102
|
+
if (!summary || typeof summary !== 'object' || Array.isArray(summary)) return null;
|
|
103
|
+
return {
|
|
104
|
+
likeCount: normalizeInteger(summary.likeCount, 0),
|
|
105
|
+
dislikeCount: normalizeInteger(summary.dislikeCount, 0),
|
|
106
|
+
viewerGave: normalizeText(summary.viewerGave, null),
|
|
107
|
+
viewerReceived: normalizeText(summary.viewerReceived, null),
|
|
88
108
|
};
|
|
89
109
|
}
|
|
90
110
|
|
|
@@ -254,6 +274,7 @@ function projectToolCandidateSummary(summary = {}, index = 0) {
|
|
|
254
274
|
score: normalizeInteger(summary.score, 0) || null,
|
|
255
275
|
summary: normalizeText(summary.summary, null),
|
|
256
276
|
expiresAt: normalizeText(summary.expiresAt, null),
|
|
277
|
+
worldFeedbackSummary: projectWorldFeedbackSummary(summary.worldFeedbackSummary),
|
|
257
278
|
};
|
|
258
279
|
}
|
|
259
280
|
|
|
@@ -277,6 +298,7 @@ function projectToolCandidateFeed(joinResult = {}) {
|
|
|
277
298
|
score: candidate.score,
|
|
278
299
|
summary: normalizeText(candidate.deliveryReason?.summary, null),
|
|
279
300
|
expiresAt: candidate.expiresAt,
|
|
301
|
+
worldFeedbackSummary: candidate.worldFeedbackSummary,
|
|
280
302
|
}, index))
|
|
281
303
|
: [];
|
|
282
304
|
|
|
@@ -354,6 +376,7 @@ export function projectToolSearchWorldResponse(searchResult = {}, { accountId =
|
|
|
354
376
|
matchReasons: projectSearchMatchReasons(item),
|
|
355
377
|
profileSnippet: summarizeProfileSnippet(item.profileSummary),
|
|
356
378
|
online: item.online === true,
|
|
379
|
+
worldFeedbackSummary: projectWorldFeedbackSummary(item.worldFeedbackSummary),
|
|
357
380
|
}))
|
|
358
381
|
: [];
|
|
359
382
|
|
|
@@ -482,7 +505,7 @@ export function projectToolFeedbackSubmissionResponse(result = {}) {
|
|
|
482
505
|
title: normalizeText(feedback.title, null),
|
|
483
506
|
accountId: normalizeText(feedback.accountId, null),
|
|
484
507
|
reporterAgentId: normalizeText(reporter.agentId, null),
|
|
485
|
-
|
|
508
|
+
reporterIdentity: normalizeText(reporter.publicIdentity?.displayIdentity, null),
|
|
486
509
|
worldId: normalizeText(context.worldId, null),
|
|
487
510
|
conversationKey: normalizeText(context.conversationKey, null),
|
|
488
511
|
turnId: normalizeText(context.turnId, null),
|
|
@@ -503,9 +526,8 @@ function projectToolAgentSummary(agent = {}) {
|
|
|
503
526
|
if (!agent || typeof agent !== 'object') return null;
|
|
504
527
|
return {
|
|
505
528
|
agentId: normalizeText(agent.agentId, null),
|
|
506
|
-
agentCode: normalizeText(agent.agentCode, null),
|
|
507
|
-
address: normalizeText(agent.address, null),
|
|
508
529
|
displayName: normalizeText(agent.displayName, null),
|
|
530
|
+
identity: normalizeText(agent.publicIdentity?.displayIdentity, null),
|
|
509
531
|
online: agent.online === true,
|
|
510
532
|
discoverable: typeof agent.discoverable === 'boolean' ? agent.discoverable : null,
|
|
511
533
|
contactable: typeof agent.contactable === 'boolean' ? agent.contactable : null,
|
|
@@ -635,6 +657,7 @@ function projectChatInboxChatItem(chat = {}) {
|
|
|
635
657
|
localSessionKey: normalizeText(chat.localSessionKey, normalizeText(chat.sessionKey, null)),
|
|
636
658
|
counterparty: projectToolAgentSummary(chat.counterparty),
|
|
637
659
|
conversation: normalizeConversationScopeDetails(chat.conversation),
|
|
660
|
+
feedbackSummary: projectConversationFeedbackSummary(chat.feedbackSummary),
|
|
638
661
|
};
|
|
639
662
|
}
|
|
640
663
|
|
|
@@ -11,6 +11,11 @@ export const CLAWORLD_BOOTSTRAP_TOOL_NAMES = Object.freeze([
|
|
|
11
11
|
'claworld_pair_agent',
|
|
12
12
|
]);
|
|
13
13
|
|
|
14
|
+
export const CLAWORLD_PROFILE_TOOL_NAMES = Object.freeze([
|
|
15
|
+
'claworld_get_public_identity',
|
|
16
|
+
'claworld_update_public_identity',
|
|
17
|
+
]);
|
|
18
|
+
|
|
14
19
|
export const CLAWORLD_FEEDBACK_TOOL_NAMES = Object.freeze([
|
|
15
20
|
'claworld_submit_feedback',
|
|
16
21
|
]);
|
|
@@ -43,6 +48,7 @@ export const CLAWORLD_RETIRED_PUBLIC_TOOL_NAMES = Object.freeze([
|
|
|
43
48
|
|
|
44
49
|
export const CLAWORLD_REGISTERED_TOOL_NAMES = Object.freeze([
|
|
45
50
|
...CLAWORLD_BOOTSTRAP_TOOL_NAMES,
|
|
51
|
+
...CLAWORLD_PROFILE_TOOL_NAMES,
|
|
46
52
|
...CLAWORLD_WORLD_TOOL_NAMES,
|
|
47
53
|
...CLAWORLD_WORLD_ADMIN_PUBLIC_TOOL_NAMES,
|
|
48
54
|
...CLAWORLD_CHAT_REQUEST_TOOL_NAMES,
|
|
@@ -67,6 +73,7 @@ export const CLAWORLD_READ_ONLY_OPENCLAW_TOOL_NAMES = Object.freeze([
|
|
|
67
73
|
|
|
68
74
|
export const CLAWORLD_PLUGIN_SMOKE_REQUIRED_TOOL_NAMES = Object.freeze([
|
|
69
75
|
...CLAWORLD_BOOTSTRAP_TOOL_NAMES,
|
|
76
|
+
...CLAWORLD_PROFILE_TOOL_NAMES,
|
|
70
77
|
...CLAWORLD_WORLD_TOOL_NAMES,
|
|
71
78
|
...CLAWORLD_WORLD_ADMIN_PUBLIC_TOOL_NAMES,
|
|
72
79
|
...CLAWORLD_CHAT_REQUEST_TOOL_NAMES,
|