@xfxstudio/claworld 2026.4.30-testing.2 → 2026.5.3-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/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/skills/claworld-a2a-channel-agent/SKILL.md +3 -46
- package/skills/claworld-join-and-chat/SKILL.md +1 -14
- package/src/lib/relay/agent-readable-markdown.js +1 -24
- package/src/openclaw/plugin/claworld-channel-plugin.js +337 -121
- package/src/openclaw/plugin/register-tooling.js +25 -5
- package/src/openclaw/plugin/register.js +212 -58
- package/src/openclaw/plugin/relay-client-shared.js +132 -51
- package/src/openclaw/plugin/relay-client.js +275 -123
- package/src/openclaw/runtime/session-routing.js +7 -3
- package/src/openclaw/runtime/working-memory.js +86 -37
|
@@ -653,7 +653,19 @@ export function projectToolAccountViewResponse({
|
|
|
653
653
|
identityPayload = null,
|
|
654
654
|
} = {}) {
|
|
655
655
|
const identityReady = identityPayload?.ready === true;
|
|
656
|
-
const
|
|
656
|
+
const activationReady = pairingPayload?.status === 'paired';
|
|
657
|
+
const bindingReady = typeof pairingPayload?.bindingReady === 'boolean'
|
|
658
|
+
? pairingPayload.bindingReady
|
|
659
|
+
: activationReady;
|
|
660
|
+
const bindingStatus = normalizeText(
|
|
661
|
+
pairingPayload?.bindingStatus,
|
|
662
|
+
activationReady
|
|
663
|
+
? (bindingReady ? 'bound' : 'identity_unresolved')
|
|
664
|
+
: 'unactivated',
|
|
665
|
+
);
|
|
666
|
+
const ready = activationReady && identityReady;
|
|
667
|
+
const relayResolved = pairingPayload?.relayAgent?.resolved ?? null;
|
|
668
|
+
const relayOnline = pairingPayload?.relayAgent?.online ?? null;
|
|
657
669
|
const resolvedShareCard = identityPayload && Object.prototype.hasOwnProperty.call(identityPayload, 'shareCard')
|
|
658
670
|
? projectToolShareCard(identityPayload.shareCard)
|
|
659
671
|
: undefined;
|
|
@@ -668,7 +680,15 @@ export function projectToolAccountViewResponse({
|
|
|
668
680
|
reason: normalizeText(pairingPayload?.reason, null),
|
|
669
681
|
bindingSource: normalizeText(pairingPayload?.bindingSource, null),
|
|
670
682
|
activation: {
|
|
671
|
-
status:
|
|
683
|
+
status: activationReady ? 'ready' : 'pending',
|
|
684
|
+
},
|
|
685
|
+
diagnostics: {
|
|
686
|
+
toolReachable: true,
|
|
687
|
+
bindingReady,
|
|
688
|
+
bindingStatus,
|
|
689
|
+
publicIdentityReady: identityReady,
|
|
690
|
+
relayPresenceResolved: relayResolved,
|
|
691
|
+
relayOnline,
|
|
672
692
|
},
|
|
673
693
|
relay: {
|
|
674
694
|
agentId: normalizeText(
|
|
@@ -678,9 +698,9 @@ export function projectToolAccountViewResponse({
|
|
|
678
698
|
displayName: normalizeText(pairingPayload?.relayAgent?.displayName, null),
|
|
679
699
|
discoverable: pairingPayload?.relayAgent?.discoverable ?? null,
|
|
680
700
|
contactable: pairingPayload?.relayAgent?.contactable ?? null,
|
|
681
|
-
online:
|
|
682
|
-
resolved:
|
|
683
|
-
bindingStatus
|
|
701
|
+
online: relayOnline,
|
|
702
|
+
resolved: relayResolved,
|
|
703
|
+
bindingStatus,
|
|
684
704
|
},
|
|
685
705
|
profile: projectToolAccountProfile(identityPayload),
|
|
686
706
|
...projectToolAccountIdentityFields(identityPayload),
|
|
@@ -143,6 +143,33 @@ const CHAT_INBOX_FILTER_STATUSES = Object.freeze([
|
|
|
143
143
|
'kickoff_failed',
|
|
144
144
|
'ended',
|
|
145
145
|
]);
|
|
146
|
+
const CHAT_INBOX_FILTER_KEYS = Object.freeze([
|
|
147
|
+
'direction',
|
|
148
|
+
'mode',
|
|
149
|
+
'status',
|
|
150
|
+
'worldId',
|
|
151
|
+
'chatRequestId',
|
|
152
|
+
'conversationKey',
|
|
153
|
+
'localSessionKey',
|
|
154
|
+
'counterpartyAgentId',
|
|
155
|
+
]);
|
|
156
|
+
const CHAT_INBOX_FILTER_KEY_SET = new Set(CHAT_INBOX_FILTER_KEYS);
|
|
157
|
+
const MANAGE_CONVERSATION_REQUEST_ONLY_QUERY_FIELDS = Object.freeze([
|
|
158
|
+
'displayName',
|
|
159
|
+
'agentCode',
|
|
160
|
+
'openingMessage',
|
|
161
|
+
]);
|
|
162
|
+
const MANAGE_CONVERSATION_FILTER_ONLY_TOP_LEVEL_FIELDS = Object.freeze([
|
|
163
|
+
'mode',
|
|
164
|
+
'status',
|
|
165
|
+
'worldId',
|
|
166
|
+
'counterpartyAgentId',
|
|
167
|
+
]);
|
|
168
|
+
const MANAGE_CONVERSATION_GET_STATE_TARGET_FIELDS = Object.freeze([
|
|
169
|
+
'chatRequestId',
|
|
170
|
+
'conversationKey',
|
|
171
|
+
'localSessionKey',
|
|
172
|
+
]);
|
|
146
173
|
|
|
147
174
|
const TERMINAL_ACCOUNT_ACTIONS = Object.freeze([
|
|
148
175
|
'view_account',
|
|
@@ -293,6 +320,117 @@ function normalizeChatInboxListFiltersInput(params = {}) {
|
|
|
293
320
|
);
|
|
294
321
|
}
|
|
295
322
|
|
|
323
|
+
function hasProvidedToolParam(params = {}, fieldId) {
|
|
324
|
+
if (!params || typeof params !== 'object') return false;
|
|
325
|
+
if (!Object.prototype.hasOwnProperty.call(params, fieldId)) return false;
|
|
326
|
+
const value = params[fieldId];
|
|
327
|
+
if (typeof value === 'string') return normalizeText(value, null) != null;
|
|
328
|
+
return value != null;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
function buildChatInboxFiltersParam({ description, worldIdProperty } = {}) {
|
|
332
|
+
return objectParam({
|
|
333
|
+
description,
|
|
334
|
+
additionalProperties: false,
|
|
335
|
+
properties: {
|
|
336
|
+
direction: stringParam({
|
|
337
|
+
description: 'Filter from the current account perspective.',
|
|
338
|
+
enumValues: CHAT_INBOX_FILTER_DIRECTIONS,
|
|
339
|
+
examples: ['outbound'],
|
|
340
|
+
}),
|
|
341
|
+
mode: stringParam({
|
|
342
|
+
description: 'Filter to direct or world-scoped chat items.',
|
|
343
|
+
enumValues: CHAT_INBOX_FILTER_MODES,
|
|
344
|
+
examples: ['world'],
|
|
345
|
+
}),
|
|
346
|
+
status: stringParam({
|
|
347
|
+
description: 'Filter to pending or terminal requests, or to chats by current status.',
|
|
348
|
+
enumValues: CHAT_INBOX_FILTER_STATUSES,
|
|
349
|
+
examples: ['active'],
|
|
350
|
+
}),
|
|
351
|
+
worldId: worldIdProperty,
|
|
352
|
+
chatRequestId: stringParam({
|
|
353
|
+
description: 'Filter to one canonical chat request id.',
|
|
354
|
+
minLength: 1,
|
|
355
|
+
examples: ['req_demo_1'],
|
|
356
|
+
}),
|
|
357
|
+
conversationKey: stringParam({
|
|
358
|
+
description: 'Filter to one canonical conversation key.',
|
|
359
|
+
minLength: 1,
|
|
360
|
+
examples: ['pair:agt_alice::agt_moza:world:dating-demo-world'],
|
|
361
|
+
}),
|
|
362
|
+
localSessionKey: stringParam({
|
|
363
|
+
description: 'Filter to one local Claworld session reference for internal tracking, summaries, or orchestration only. Not a transport address for sending a user message to the peer.',
|
|
364
|
+
minLength: 1,
|
|
365
|
+
examples: ['conversation:pair:agt_alice::agt_moza:world:dating-demo-world'],
|
|
366
|
+
}),
|
|
367
|
+
counterpartyAgentId: stringParam({
|
|
368
|
+
description: 'Filter to one counterparty agentId.',
|
|
369
|
+
minLength: 1,
|
|
370
|
+
examples: ['agt_alice'],
|
|
371
|
+
}),
|
|
372
|
+
},
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
function validateChatInboxFilterInput(filters = {}, action) {
|
|
377
|
+
const source = normalizeObject(filters, {}) || {};
|
|
378
|
+
for (const key of Object.keys(source)) {
|
|
379
|
+
if (CHAT_INBOX_FILTER_KEY_SET.has(key)) continue;
|
|
380
|
+
requireManageWorldField(`filters.${key}`, `filters.${key} is not supported for action=${action}`);
|
|
381
|
+
}
|
|
382
|
+
return source;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
function normalizeManageConversationInboxQuery(params = {}, action) {
|
|
386
|
+
const normalizedAction = normalizeTerminalConversationAction(action, 'list_related');
|
|
387
|
+
const filters = validateChatInboxFilterInput(params.filters, normalizedAction);
|
|
388
|
+
|
|
389
|
+
const requestOnlyField = MANAGE_CONVERSATION_REQUEST_ONLY_QUERY_FIELDS.find((fieldId) => hasProvidedToolParam(params, fieldId));
|
|
390
|
+
if (requestOnlyField) {
|
|
391
|
+
requireManageWorldField(requestOnlyField, `${requestOnlyField} is only supported for action=request`);
|
|
392
|
+
}
|
|
393
|
+
if (hasProvidedToolParam(params, 'limit')) {
|
|
394
|
+
requireManageWorldField('limit', `limit is not supported for action=${normalizedAction}`);
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
const filterOnlyField = MANAGE_CONVERSATION_FILTER_ONLY_TOP_LEVEL_FIELDS.find((fieldId) => hasProvidedToolParam(params, fieldId));
|
|
398
|
+
if (filterOnlyField) {
|
|
399
|
+
requireManageWorldField(
|
|
400
|
+
filterOnlyField,
|
|
401
|
+
`${filterOnlyField} must be passed as filters.${filterOnlyField} for action=${normalizedAction}`,
|
|
402
|
+
);
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
if (normalizedAction !== 'get_state') {
|
|
406
|
+
const getStateOnlyField = MANAGE_CONVERSATION_GET_STATE_TARGET_FIELDS.find((fieldId) => hasProvidedToolParam(params, fieldId));
|
|
407
|
+
if (getStateOnlyField) {
|
|
408
|
+
requireManageWorldField(
|
|
409
|
+
getStateOnlyField,
|
|
410
|
+
`${getStateOnlyField} must be passed as filters.${getStateOnlyField} for action=${normalizedAction}`,
|
|
411
|
+
);
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
const mergedFilters = {
|
|
416
|
+
...filters,
|
|
417
|
+
...(!Object.prototype.hasOwnProperty.call(filters, 'direction') && hasProvidedToolParam(params, 'direction')
|
|
418
|
+
? { direction: params.direction }
|
|
419
|
+
: {}),
|
|
420
|
+
...(normalizedAction === 'get_state'
|
|
421
|
+
? Object.fromEntries(
|
|
422
|
+
MANAGE_CONVERSATION_GET_STATE_TARGET_FIELDS
|
|
423
|
+
.filter((fieldId) => (
|
|
424
|
+
!Object.prototype.hasOwnProperty.call(filters, fieldId)
|
|
425
|
+
&& hasProvidedToolParam(params, fieldId)
|
|
426
|
+
))
|
|
427
|
+
.map((fieldId) => [fieldId, params[fieldId]]),
|
|
428
|
+
)
|
|
429
|
+
: {}),
|
|
430
|
+
};
|
|
431
|
+
return normalizeChatInboxListFiltersInput({ filters: mergedFilters });
|
|
432
|
+
}
|
|
433
|
+
|
|
296
434
|
function parseToolResultPayload(result = null) {
|
|
297
435
|
const text = result?.content?.[0]?.text;
|
|
298
436
|
if (typeof text !== 'string') return null;
|
|
@@ -925,10 +1063,27 @@ function createTerminalToolAdapters(api, plugin, internalTools) {
|
|
|
925
1063
|
agentCode: stringParam({ description: 'Target public agent code for request.', minLength: 1 }),
|
|
926
1064
|
openingMessage: stringParam({ description: 'Request/re-engagement kickoff message.', minLength: 1 }),
|
|
927
1065
|
worldId: worldIdProperty,
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
1066
|
+
direction: stringParam({
|
|
1067
|
+
description: 'Top-level alias for filters.direction on action=list_related/get_state.',
|
|
1068
|
+
enumValues: CHAT_INBOX_FILTER_DIRECTIONS,
|
|
1069
|
+
examples: ['outbound'],
|
|
1070
|
+
}),
|
|
1071
|
+
filters: buildChatInboxFiltersParam({
|
|
1072
|
+
description: 'Inbox filters for action=list_related/get_state.',
|
|
1073
|
+
worldIdProperty,
|
|
1074
|
+
}),
|
|
1075
|
+
chatRequestId: stringParam({
|
|
1076
|
+
description: 'Request id for action=accept/reject, or a top-level get_state convenience target that normalizes to filters.chatRequestId.',
|
|
1077
|
+
minLength: 1,
|
|
1078
|
+
}),
|
|
1079
|
+
conversationKey: stringParam({
|
|
1080
|
+
description: 'Conversation key for action=close, or a top-level get_state convenience target that normalizes to filters.conversationKey.',
|
|
1081
|
+
minLength: 1,
|
|
1082
|
+
}),
|
|
1083
|
+
localSessionKey: stringParam({
|
|
1084
|
+
description: 'Local conversation session key for action=close, or a top-level get_state convenience target that normalizes to filters.localSessionKey.',
|
|
1085
|
+
minLength: 1,
|
|
1086
|
+
}),
|
|
932
1087
|
},
|
|
933
1088
|
}),
|
|
934
1089
|
async execute(toolCallId, params = {}) {
|
|
@@ -940,10 +1095,19 @@ function createTerminalToolAdapters(api, plugin, internalTools) {
|
|
|
940
1095
|
});
|
|
941
1096
|
return rewriteToolResultName(result, manageConversationsTool, action);
|
|
942
1097
|
}
|
|
943
|
-
if (
|
|
1098
|
+
if (action === 'list_related' || action === 'get_state') {
|
|
1099
|
+
const filters = normalizeManageConversationInboxQuery(params, action);
|
|
1100
|
+
const result = await requireTerminalTool(internalTools, 'claworld_chat_inbox').execute(toolCallId, {
|
|
1101
|
+
...params,
|
|
1102
|
+
action: 'list',
|
|
1103
|
+
...(Object.keys(filters).length > 0 ? { filters } : {}),
|
|
1104
|
+
});
|
|
1105
|
+
return rewriteToolResultName(result, manageConversationsTool, action);
|
|
1106
|
+
}
|
|
1107
|
+
if (action === 'accept' || action === 'reject') {
|
|
944
1108
|
const result = await requireTerminalTool(internalTools, 'claworld_chat_inbox').execute(toolCallId, {
|
|
945
1109
|
...params,
|
|
946
|
-
action
|
|
1110
|
+
action,
|
|
947
1111
|
});
|
|
948
1112
|
return rewriteToolResultName(result, manageConversationsTool, action);
|
|
949
1113
|
}
|
|
@@ -1691,45 +1855,10 @@ function buildRegisteredTools(api, plugin) {
|
|
|
1691
1855
|
examples: ['list', 'accept', 'reject'],
|
|
1692
1856
|
}),
|
|
1693
1857
|
filters: objectParam({
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
enumValues: CHAT_INBOX_FILTER_DIRECTIONS,
|
|
1699
|
-
examples: ['outbound'],
|
|
1700
|
-
}),
|
|
1701
|
-
mode: stringParam({
|
|
1702
|
-
description: 'Filter to direct or world-scoped chat items.',
|
|
1703
|
-
enumValues: CHAT_INBOX_FILTER_MODES,
|
|
1704
|
-
examples: ['world'],
|
|
1705
|
-
}),
|
|
1706
|
-
status: stringParam({
|
|
1707
|
-
description: 'Filter to pending or terminal requests, or to chats by current status.',
|
|
1708
|
-
enumValues: CHAT_INBOX_FILTER_STATUSES,
|
|
1709
|
-
examples: ['active'],
|
|
1710
|
-
}),
|
|
1711
|
-
worldId: worldIdProperty,
|
|
1712
|
-
chatRequestId: stringParam({
|
|
1713
|
-
description: 'Filter to one canonical chat request id.',
|
|
1714
|
-
minLength: 1,
|
|
1715
|
-
examples: ['req_demo_1'],
|
|
1716
|
-
}),
|
|
1717
|
-
conversationKey: stringParam({
|
|
1718
|
-
description: 'Filter to one canonical conversation key.',
|
|
1719
|
-
minLength: 1,
|
|
1720
|
-
examples: ['pair:agt_alice::agt_moza:world:dating-demo-world'],
|
|
1721
|
-
}),
|
|
1722
|
-
localSessionKey: stringParam({
|
|
1723
|
-
description: 'Filter to one local Claworld session reference for internal tracking, summaries, or orchestration only. Not a transport address for sending a user message to the peer.',
|
|
1724
|
-
minLength: 1,
|
|
1725
|
-
examples: ['conversation:pair:agt_alice::agt_moza:world:dating-demo-world'],
|
|
1726
|
-
}),
|
|
1727
|
-
counterpartyAgentId: stringParam({
|
|
1728
|
-
description: 'Filter to one counterparty agentId.',
|
|
1729
|
-
minLength: 1,
|
|
1730
|
-
examples: ['agt_alice'],
|
|
1731
|
-
}),
|
|
1732
|
-
},
|
|
1858
|
+
...buildChatInboxFiltersParam({
|
|
1859
|
+
description: 'Optional list filters for query mode. Omit to review the full inbox across inbound and outbound items.',
|
|
1860
|
+
worldIdProperty,
|
|
1861
|
+
}),
|
|
1733
1862
|
}),
|
|
1734
1863
|
chatRequestId: stringParam({
|
|
1735
1864
|
description: 'Canonical chat request id returned by claworld_chat_inbox pendingRequests. Required for action=accept or action=reject.',
|
|
@@ -1969,7 +2098,16 @@ function buildRegisteredTools(api, plugin) {
|
|
|
1969
2098
|
expiresInSeconds: params.expiresInSeconds ?? null,
|
|
1970
2099
|
});
|
|
1971
2100
|
const pairedAgentId = identityPayload?.agentId || runtimeConfig.relay?.agentId || null;
|
|
1972
|
-
const
|
|
2101
|
+
const pairedRuntimeConfig = pairedAgentId
|
|
2102
|
+
? {
|
|
2103
|
+
...runtimeConfig,
|
|
2104
|
+
relay: {
|
|
2105
|
+
...(runtimeConfig.relay && typeof runtimeConfig.relay === 'object' ? runtimeConfig.relay : {}),
|
|
2106
|
+
agentId: pairedAgentId,
|
|
2107
|
+
},
|
|
2108
|
+
}
|
|
2109
|
+
: runtimeConfig;
|
|
2110
|
+
const relayAgentFallback = pairedAgentId
|
|
1973
2111
|
? {
|
|
1974
2112
|
agentId: pairedAgentId,
|
|
1975
2113
|
displayName: normalizeText(
|
|
@@ -1990,23 +2128,39 @@ function buildRegisteredTools(api, plugin) {
|
|
|
1990
2128
|
|| runtimeConfig.relay?.appToken
|
|
1991
2129
|
|| runtimeConfig.relay?.credentialToken,
|
|
1992
2130
|
);
|
|
2131
|
+
const activationReady = hasConfiguredAppToken;
|
|
2132
|
+
const bindingReady = activationReady && Boolean(pairedAgentId);
|
|
2133
|
+
const bindingStatus = activationReady
|
|
2134
|
+
? (bindingReady ? 'bound' : 'identity_unresolved')
|
|
2135
|
+
: 'unactivated';
|
|
2136
|
+
let relayAgent = relayAgentFallback;
|
|
2137
|
+
if (hasConfiguredAppToken && pairedAgentId && typeof plugin.helpers?.pairing?.resolveAgentIdentity === 'function') {
|
|
2138
|
+
const resolvedRelayAgent = await plugin.helpers.pairing.resolveAgentIdentity({
|
|
2139
|
+
cfg,
|
|
2140
|
+
accountId,
|
|
2141
|
+
runtimeConfig: pairedRuntimeConfig,
|
|
2142
|
+
agentId: pairedAgentId,
|
|
2143
|
+
});
|
|
2144
|
+
if (resolvedRelayAgent && typeof resolvedRelayAgent === 'object') {
|
|
2145
|
+
relayAgent = {
|
|
2146
|
+
...relayAgentFallback,
|
|
2147
|
+
...resolvedRelayAgent,
|
|
2148
|
+
agentId: normalizeText(resolvedRelayAgent.agentId, pairedAgentId),
|
|
2149
|
+
displayName: normalizeText(resolvedRelayAgent.displayName, relayAgentFallback?.displayName ?? null),
|
|
2150
|
+
};
|
|
2151
|
+
}
|
|
2152
|
+
}
|
|
1993
2153
|
const pairingPayload = {
|
|
1994
|
-
status:
|
|
1995
|
-
|
|
2154
|
+
status: activationReady ? 'paired' : 'unpaired',
|
|
2155
|
+
bindingReady,
|
|
2156
|
+
bindingStatus,
|
|
2157
|
+
reason: activationReady
|
|
1996
2158
|
? (pairedAgentId ? null : 'missing_agent_id')
|
|
1997
2159
|
: 'missing_app_token',
|
|
1998
|
-
bindingSource:
|
|
2160
|
+
bindingSource: activationReady
|
|
1999
2161
|
? 'configured_app_token'
|
|
2000
2162
|
: (runtimeConfig.registration?.enabled === true ? 'registration_pending' : 'unbound'),
|
|
2001
|
-
runtimeConfig:
|
|
2002
|
-
? {
|
|
2003
|
-
...runtimeConfig,
|
|
2004
|
-
relay: {
|
|
2005
|
-
...(runtimeConfig.relay && typeof runtimeConfig.relay === 'object' ? runtimeConfig.relay : {}),
|
|
2006
|
-
agentId: pairedAgentId,
|
|
2007
|
-
},
|
|
2008
|
-
}
|
|
2009
|
-
: runtimeConfig,
|
|
2163
|
+
runtimeConfig: pairedRuntimeConfig,
|
|
2010
2164
|
relayAgent,
|
|
2011
2165
|
};
|
|
2012
2166
|
return buildToolResult(projectToolAccountViewResponse({
|
|
@@ -5,6 +5,51 @@ export const STALE_CONNECTION_CLOSE_CODE = 4002;
|
|
|
5
5
|
export const TERMINAL_CLOSE_REASONS = new Set(['duplicate_connection_replaced', 'stale_connection']);
|
|
6
6
|
export const DEFAULT_REPLY_ACK_TIMEOUT_MS = 5000;
|
|
7
7
|
|
|
8
|
+
function cloneObject(value, fallback = {}) {
|
|
9
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) return { ...fallback };
|
|
10
|
+
return { ...value };
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function normalizeEnvelopeText(value, fallback = null) {
|
|
14
|
+
if (value == null) return fallback;
|
|
15
|
+
const normalized = String(value).trim();
|
|
16
|
+
return normalized || fallback;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function resolveEnvelopeMessageId(data = {}, payload = {}) {
|
|
20
|
+
const notification = payload.notification && typeof payload.notification === 'object' && !Array.isArray(payload.notification)
|
|
21
|
+
? payload.notification
|
|
22
|
+
: data.notification && typeof data.notification === 'object' && !Array.isArray(data.notification)
|
|
23
|
+
? data.notification
|
|
24
|
+
: {};
|
|
25
|
+
const metadata = data.metadata && typeof data.metadata === 'object' && !Array.isArray(data.metadata)
|
|
26
|
+
? data.metadata
|
|
27
|
+
: payload.metadata && typeof payload.metadata === 'object' && !Array.isArray(payload.metadata)
|
|
28
|
+
? payload.metadata
|
|
29
|
+
: {};
|
|
30
|
+
const candidates = [
|
|
31
|
+
data.deliveryId,
|
|
32
|
+
data.inboxItemId,
|
|
33
|
+
data.messageId,
|
|
34
|
+
data.eventId,
|
|
35
|
+
data.notificationId,
|
|
36
|
+
payload.deliveryId,
|
|
37
|
+
payload.inboxItemId,
|
|
38
|
+
payload.messageId,
|
|
39
|
+
payload.eventId,
|
|
40
|
+
payload.notificationId,
|
|
41
|
+
metadata.messageId,
|
|
42
|
+
metadata.eventId,
|
|
43
|
+
metadata.notificationId,
|
|
44
|
+
notification.notificationId,
|
|
45
|
+
];
|
|
46
|
+
for (const candidate of candidates) {
|
|
47
|
+
const normalized = normalizeEnvelopeText(candidate, null);
|
|
48
|
+
if (normalized) return normalized;
|
|
49
|
+
}
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
|
|
8
53
|
export function normalizeRelayWebSocketUrl(serverUrl) {
|
|
9
54
|
const parsed = new URL(serverUrl);
|
|
10
55
|
if (parsed.protocol === 'http:') parsed.protocol = 'ws:';
|
|
@@ -23,63 +68,99 @@ export function normalizeRelayWebSocketUrl(serverUrl) {
|
|
|
23
68
|
|
|
24
69
|
export function buildInboundEnvelope(message = {}) {
|
|
25
70
|
const data = message.data || {};
|
|
71
|
+
const directPayload = data.payload && typeof data.payload === 'object' && !Array.isArray(data.payload)
|
|
72
|
+
? { ...data.payload }
|
|
73
|
+
: {};
|
|
26
74
|
const metadata = data.metadata && typeof data.metadata === 'object' && !Array.isArray(data.metadata)
|
|
27
75
|
? { ...data.metadata }
|
|
28
|
-
:
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
76
|
+
: directPayload.metadata && typeof directPayload.metadata === 'object' && !Array.isArray(directPayload.metadata)
|
|
77
|
+
? { ...directPayload.metadata }
|
|
78
|
+
: cloneObject(data.meta, {});
|
|
79
|
+
const payloadEventType = normalizeEnvelopeText(directPayload.eventType, null);
|
|
80
|
+
const dataEventType = normalizeEnvelopeText(data.eventType, null);
|
|
81
|
+
const eventType = dataEventType
|
|
82
|
+
|| payloadEventType
|
|
83
|
+
|| (message.event === 'delivery' ? 'delivery' : normalizeEnvelopeText(message.event, null));
|
|
84
|
+
const payload = Object.keys(directPayload).length > 0
|
|
85
|
+
? { ...directPayload }
|
|
86
|
+
: cloneObject(data, {});
|
|
87
|
+
if (Object.keys(directPayload).length > 0) {
|
|
88
|
+
for (const key of [
|
|
89
|
+
'eventType',
|
|
90
|
+
'eventName',
|
|
91
|
+
'sessionKind',
|
|
92
|
+
'sessionKey',
|
|
93
|
+
'targetSessionKey',
|
|
94
|
+
'targetAgentId',
|
|
95
|
+
'text',
|
|
96
|
+
'body',
|
|
97
|
+
'notification',
|
|
98
|
+
'conversationKey',
|
|
99
|
+
'worldId',
|
|
100
|
+
]) {
|
|
101
|
+
if (payload[key] == null && data[key] != null) payload[key] = data[key];
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
const notification = payload.notification && typeof payload.notification === 'object' && !Array.isArray(payload.notification)
|
|
105
|
+
? payload.notification
|
|
106
|
+
: data.notification && typeof data.notification === 'object' && !Array.isArray(data.notification)
|
|
32
107
|
? data.notification
|
|
33
108
|
: {};
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
metadata: {
|
|
66
|
-
...metadata,
|
|
67
|
-
relayEvent: normalizeOptionalText(message.event),
|
|
68
|
-
inboxItemId: normalizeOptionalText(data.inboxItemId) || null,
|
|
69
|
-
},
|
|
70
|
-
};
|
|
71
|
-
}
|
|
109
|
+
const targetAgentId = normalizeEnvelopeText(
|
|
110
|
+
data.targetAgentId,
|
|
111
|
+
normalizeEnvelopeText(
|
|
112
|
+
payload.targetAgentId,
|
|
113
|
+
normalizeEnvelopeText(notification.targetAgentId, normalizeEnvelopeText(metadata.targetAgentId, null)),
|
|
114
|
+
),
|
|
115
|
+
);
|
|
116
|
+
const sessionKey = normalizeEnvelopeText(
|
|
117
|
+
data.sessionKey,
|
|
118
|
+
normalizeEnvelopeText(
|
|
119
|
+
payload.sessionKey,
|
|
120
|
+
normalizeEnvelopeText(
|
|
121
|
+
data.targetSessionKey,
|
|
122
|
+
normalizeEnvelopeText(
|
|
123
|
+
payload.targetSessionKey,
|
|
124
|
+
normalizeEnvelopeText(
|
|
125
|
+
notification.targetSessionKey,
|
|
126
|
+
normalizeEnvelopeText(metadata.sessionKey, targetAgentId ? `management:${targetAgentId}` : null),
|
|
127
|
+
),
|
|
128
|
+
),
|
|
129
|
+
),
|
|
130
|
+
),
|
|
131
|
+
);
|
|
132
|
+
const isDeliveryEvent = message.event === 'delivery';
|
|
133
|
+
const isRoutableEvent = Boolean(eventType && sessionKey);
|
|
134
|
+
if (!isDeliveryEvent && !isRoutableEvent) return null;
|
|
135
|
+
const deliveryId = resolveEnvelopeMessageId(data, payload);
|
|
136
|
+
const eventName = normalizeEnvelopeText(
|
|
137
|
+
data.eventName,
|
|
138
|
+
normalizeEnvelopeText(payload.eventName, isDeliveryEvent ? null : normalizeEnvelopeText(message.event, null)),
|
|
139
|
+
);
|
|
72
140
|
return {
|
|
73
|
-
eventType:
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
141
|
+
eventType: eventType || 'delivery',
|
|
142
|
+
eventName,
|
|
143
|
+
eventId: deliveryId,
|
|
144
|
+
deliveryId,
|
|
145
|
+
sessionKey,
|
|
146
|
+
targetAgentId,
|
|
147
|
+
conversationKey: normalizeEnvelopeText(
|
|
148
|
+
data.conversationKey,
|
|
149
|
+
normalizeEnvelopeText(payload.conversationKey, normalizeEnvelopeText(notification.relatedObjects?.conversationKey, null)),
|
|
150
|
+
),
|
|
151
|
+
worldId: normalizeEnvelopeText(
|
|
152
|
+
data.worldId,
|
|
153
|
+
normalizeEnvelopeText(payload.worldId, normalizeEnvelopeText(notification.relatedObjects?.worldId, null)),
|
|
154
|
+
),
|
|
155
|
+
createdAt: data.createdAt || payload.createdAt || data.availableAt || payload.availableAt || notification.createdAt || null,
|
|
156
|
+
updatedAt: data.updatedAt || payload.updatedAt || notification.updatedAt || null,
|
|
78
157
|
turnCreatedAt: data.turnCreatedAt || null,
|
|
79
|
-
payload
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
158
|
+
payload,
|
|
159
|
+
metadata: {
|
|
160
|
+
...metadata,
|
|
161
|
+
relayEvent: normalizeEnvelopeText(message.event, null),
|
|
162
|
+
inboxItemId: normalizeEnvelopeText(data.inboxItemId, normalizeEnvelopeText(payload.inboxItemId, null)),
|
|
163
|
+
},
|
|
83
164
|
};
|
|
84
165
|
}
|
|
85
166
|
|