@xfxstudio/claworld 2026.4.16-testing.1 → 2026.4.16-testing.2
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 +3 -3
- package/index.js +50 -0
- package/openclaw.plugin.json +1 -1
- package/package.json +4 -1
- package/setup-entry.js +6 -0
- package/skills/claworld-a2a-channel-agent/SKILL.md +218 -0
- package/skills/claworld-help/SKILL.md +304 -0
- package/skills/claworld-join-and-chat/SKILL.md +526 -0
- package/skills/claworld-manage-worlds/SKILL.md +292 -0
- package/skills/claworld-manage-worlds/references/world-context-templates.md +145 -0
- package/src/lib/chat-request.js +366 -0
- package/src/lib/public-identity.js +175 -0
- package/src/lib/relay/agent-readable-markdown.js +385 -0
- package/src/lib/relay/kickoff-progress.js +162 -0
- package/src/lib/relay/kickoff-text.js +191 -0
- package/src/lib/relay/shared.js +30 -0
- package/src/lib/runtime-errors.js +149 -0
- package/src/openclaw/index.js +51 -0
- package/src/openclaw/plugin/account-identity.js +73 -0
- package/src/openclaw/plugin/claworld-channel-plugin.js +3499 -0
- package/src/openclaw/plugin/config-schema.js +392 -0
- package/src/openclaw/plugin/lifecycle.js +114 -0
- package/src/openclaw/plugin/managed-config.js +1054 -0
- package/src/openclaw/plugin/onboarding.js +312 -0
- package/src/openclaw/plugin/register-tooling.js +728 -0
- package/src/openclaw/plugin/register.js +1616 -0
- package/src/openclaw/plugin/relay-client-shared.js +146 -0
- package/src/openclaw/plugin/relay-client.js +1469 -0
- package/src/openclaw/plugin/runtime-backup.js +105 -0
- package/src/openclaw/plugin/runtime.js +12 -0
- package/src/openclaw/plugin-version.js +67 -0
- package/src/openclaw/protocol/relay-event-protocol.js +43 -0
- package/src/openclaw/runtime/backend-error-context.js +91 -0
- package/src/openclaw/runtime/canonical-result-builder.js +126 -0
- package/src/openclaw/runtime/demo-session-bootstrap.js +32 -0
- package/src/openclaw/runtime/feedback-helper.js +145 -0
- package/src/openclaw/runtime/inbound-session-router.js +44 -0
- package/src/openclaw/runtime/outbound-session-bridge.js +29 -0
- package/src/openclaw/runtime/product-shell-helper.js +931 -0
- package/src/openclaw/runtime/runtime-path.js +19 -0
- package/src/openclaw/runtime/system-message-orchestrator.js +1 -0
- package/src/openclaw/runtime/tool-contracts.js +939 -0
- package/src/openclaw/runtime/tool-inventory.js +83 -0
- package/src/openclaw/runtime/world-membership-helper.js +320 -0
- package/src/openclaw/runtime/world-moderation-helper.js +508 -0
- package/src/product-shell/contracts/chat-request-approval-policy.js +93 -0
- package/src/product-shell/contracts/world-orchestration.js +734 -0
- package/src/product-shell/orchestration/world-conversation-text.js +229 -0
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
import { createKickoffBrief } from './relay/kickoff-text.js';
|
|
2
|
+
import { normalizeAcceptedChatKickoffRecord } from './relay/kickoff-progress.js';
|
|
3
|
+
|
|
4
|
+
function normalizeText(value, fallback = null) {
|
|
5
|
+
if (value == null) return fallback;
|
|
6
|
+
const normalized = String(value).trim();
|
|
7
|
+
return normalized || fallback;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function cloneJsonObject(value) {
|
|
11
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) return null;
|
|
12
|
+
try {
|
|
13
|
+
const cloned = JSON.parse(JSON.stringify(value));
|
|
14
|
+
if (!cloned || typeof cloned !== 'object' || Array.isArray(cloned)) return null;
|
|
15
|
+
return cloned;
|
|
16
|
+
} catch {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function normalizeChatRequestConversation(input = {}) {
|
|
22
|
+
if (!input || typeof input !== 'object' || Array.isArray(input)) return {};
|
|
23
|
+
const conversation = {
|
|
24
|
+
...(normalizeText(input.worldId, null) ? { worldId: normalizeText(input.worldId, null) } : {}),
|
|
25
|
+
...(normalizeText(input.scope, null)
|
|
26
|
+
? { scope: normalizeText(input.scope, null) }
|
|
27
|
+
: {}),
|
|
28
|
+
...(normalizeText(input.conversationId, null) ? { conversationId: normalizeText(input.conversationId, null) } : {}),
|
|
29
|
+
...(normalizeText(input.threadId, null) ? { threadId: normalizeText(input.threadId, null) } : {}),
|
|
30
|
+
...(normalizeText(input.conversationKey, null) ? { conversationKey: normalizeText(input.conversationKey, null) } : {}),
|
|
31
|
+
...(normalizeText(input.sessionKey, null) ? { sessionKey: normalizeText(input.sessionKey, null) } : {}),
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const episode = cloneJsonObject(input.episode);
|
|
35
|
+
if (episode) {
|
|
36
|
+
conversation.episode = episode;
|
|
37
|
+
}
|
|
38
|
+
const worldContextText = normalizeText(
|
|
39
|
+
input.worldContextText,
|
|
40
|
+
normalizeText(input.worldContext?.worldContextText, normalizeText(input.worldContext?.text, null)),
|
|
41
|
+
);
|
|
42
|
+
if (worldContextText) {
|
|
43
|
+
conversation.worldContextText = worldContextText;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return conversation;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function normalizeChatRequestOrigin(origin = {}, { fallbackType = null } = {}) {
|
|
50
|
+
if (!origin || typeof origin !== 'object' || Array.isArray(origin)) {
|
|
51
|
+
const type = normalizeText(fallbackType, null);
|
|
52
|
+
return type ? { type } : null;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const normalized = {
|
|
56
|
+
...(normalizeText(origin.type, normalizeText(fallbackType, null))
|
|
57
|
+
? { type: normalizeText(origin.type, normalizeText(fallbackType, null)) }
|
|
58
|
+
: {}),
|
|
59
|
+
...(normalizeText(origin.broadcastId, null) ? { broadcastId: normalizeText(origin.broadcastId, null) } : {}),
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
return Object.keys(normalized).length > 0 ? normalized : null;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function normalizeChatRequestBroadcast(input = {}) {
|
|
66
|
+
if (!input || typeof input !== 'object' || Array.isArray(input)) return null;
|
|
67
|
+
const normalized = {
|
|
68
|
+
...(normalizeText(input.broadcastId, null) ? { broadcastId: normalizeText(input.broadcastId, null) } : {}),
|
|
69
|
+
...(normalizeText(input.worldId, null) ? { worldId: normalizeText(input.worldId, null) } : {}),
|
|
70
|
+
...(normalizeText(input.audience, null) ? { audience: normalizeText(input.audience, null) } : {}),
|
|
71
|
+
...(normalizeText(input.senderRole, null) ? { senderRole: normalizeText(input.senderRole, null) } : {}),
|
|
72
|
+
...(normalizeText(input.eligibility, null) ? { eligibility: normalizeText(input.eligibility, null) } : {}),
|
|
73
|
+
...(typeof input.excludeSelf === 'boolean' ? { excludeSelf: input.excludeSelf } : {}),
|
|
74
|
+
};
|
|
75
|
+
return Object.keys(normalized).length > 0 ? normalized : null;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function normalizeChatRequestFollowUp(input = {}) {
|
|
79
|
+
if (!input || typeof input !== 'object' || Array.isArray(input)) return null;
|
|
80
|
+
const normalized = {
|
|
81
|
+
...(normalizeText(input.sessionKey, null) ? { sessionKey: normalizeText(input.sessionKey, null) } : {}),
|
|
82
|
+
};
|
|
83
|
+
return Object.keys(normalized).length > 0 ? normalized : null;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export function normalizeChatRequestOpeningPayload(input = null) {
|
|
87
|
+
if (!input || typeof input !== 'object' || Array.isArray(input)) return null;
|
|
88
|
+
const payload = cloneJsonObject(input);
|
|
89
|
+
if (!payload) return null;
|
|
90
|
+
if (typeof payload.text === 'string') {
|
|
91
|
+
payload.text = payload.text.trim();
|
|
92
|
+
if (!payload.text) delete payload.text;
|
|
93
|
+
}
|
|
94
|
+
if (typeof payload.source === 'string') {
|
|
95
|
+
payload.source = payload.source.trim() || undefined;
|
|
96
|
+
}
|
|
97
|
+
return Object.keys(payload).length > 0 ? payload : null;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export function resolveChatRequestOpeningMessage({
|
|
101
|
+
openingMessage = null,
|
|
102
|
+
openingPayload = null,
|
|
103
|
+
requestContext = null,
|
|
104
|
+
} = {}) {
|
|
105
|
+
return normalizeText(
|
|
106
|
+
openingMessage,
|
|
107
|
+
normalizeText(openingPayload?.text, normalizeText(requestContext?.message, null)),
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function resolveKickoffBriefSource({
|
|
112
|
+
kickoffBrief = null,
|
|
113
|
+
openingPayload = null,
|
|
114
|
+
requestContext = null,
|
|
115
|
+
source = null,
|
|
116
|
+
} = {}) {
|
|
117
|
+
const normalizedContext = requestContext && typeof requestContext === 'object' && !Array.isArray(requestContext)
|
|
118
|
+
? requestContext
|
|
119
|
+
: {};
|
|
120
|
+
const explicitKickoffBrief = kickoffBrief && typeof kickoffBrief === 'object' && !Array.isArray(kickoffBrief)
|
|
121
|
+
? kickoffBrief
|
|
122
|
+
: null;
|
|
123
|
+
const contextKickoffBrief = normalizedContext.kickoffBrief && typeof normalizedContext.kickoffBrief === 'object' && !Array.isArray(normalizedContext.kickoffBrief)
|
|
124
|
+
? normalizedContext.kickoffBrief
|
|
125
|
+
: null;
|
|
126
|
+
const fallbackSource = normalizeText(source, null) === 'world_broadcast'
|
|
127
|
+
? 'world_broadcast_brief'
|
|
128
|
+
: 'chat_request_brief';
|
|
129
|
+
return normalizeText(
|
|
130
|
+
explicitKickoffBrief?.source,
|
|
131
|
+
normalizeText(
|
|
132
|
+
contextKickoffBrief?.source,
|
|
133
|
+
normalizeText(
|
|
134
|
+
openingPayload?.source,
|
|
135
|
+
normalizeText(normalizedContext.openingPayload?.source, fallbackSource),
|
|
136
|
+
),
|
|
137
|
+
),
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function resolveChatRequestKickoffBrief({
|
|
142
|
+
kickoffBrief = null,
|
|
143
|
+
openingMessage = null,
|
|
144
|
+
openingPayload = null,
|
|
145
|
+
requestContext = null,
|
|
146
|
+
source = null,
|
|
147
|
+
} = {}) {
|
|
148
|
+
const normalizedContext = requestContext && typeof requestContext === 'object' && !Array.isArray(requestContext)
|
|
149
|
+
? requestContext
|
|
150
|
+
: {};
|
|
151
|
+
const explicitKickoffBrief = kickoffBrief && typeof kickoffBrief === 'object' && !Array.isArray(kickoffBrief)
|
|
152
|
+
? kickoffBrief
|
|
153
|
+
: null;
|
|
154
|
+
const contextKickoffBrief = normalizedContext.kickoffBrief && typeof normalizedContext.kickoffBrief === 'object' && !Array.isArray(normalizedContext.kickoffBrief)
|
|
155
|
+
? normalizedContext.kickoffBrief
|
|
156
|
+
: null;
|
|
157
|
+
const normalizedOpeningPayload = normalizeChatRequestOpeningPayload(
|
|
158
|
+
explicitKickoffBrief?.payload
|
|
159
|
+
?? contextKickoffBrief?.payload
|
|
160
|
+
?? openingPayload
|
|
161
|
+
?? normalizedContext.openingPayload,
|
|
162
|
+
);
|
|
163
|
+
const normalizedOpeningMessage = resolveChatRequestOpeningMessage({
|
|
164
|
+
openingMessage:
|
|
165
|
+
explicitKickoffBrief?.text
|
|
166
|
+
?? contextKickoffBrief?.text
|
|
167
|
+
?? openingMessage,
|
|
168
|
+
openingPayload: normalizedOpeningPayload,
|
|
169
|
+
requestContext: normalizedContext,
|
|
170
|
+
});
|
|
171
|
+
return createKickoffBrief({
|
|
172
|
+
text: normalizedOpeningMessage,
|
|
173
|
+
payload: explicitKickoffBrief?.payload ?? contextKickoffBrief?.payload ?? normalizedOpeningPayload,
|
|
174
|
+
source: resolveKickoffBriefSource({
|
|
175
|
+
kickoffBrief: explicitKickoffBrief ?? contextKickoffBrief,
|
|
176
|
+
openingPayload: normalizedOpeningPayload,
|
|
177
|
+
requestContext: normalizedContext,
|
|
178
|
+
source,
|
|
179
|
+
}),
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
export function normalizeChatRequestInput({ requestContext = {}, source = null } = {}) {
|
|
184
|
+
const normalizedContext = requestContext && typeof requestContext === 'object' && !Array.isArray(requestContext)
|
|
185
|
+
? requestContext
|
|
186
|
+
: {};
|
|
187
|
+
const conversationSource = normalizedContext.conversation && typeof normalizedContext.conversation === 'object' && !Array.isArray(normalizedContext.conversation)
|
|
188
|
+
? normalizedContext.conversation
|
|
189
|
+
: {};
|
|
190
|
+
const conversation = normalizeChatRequestConversation({
|
|
191
|
+
...conversationSource,
|
|
192
|
+
conversationKey: normalizedContext.conversationKey ?? conversationSource.conversationKey,
|
|
193
|
+
sessionKey: normalizedContext.sessionKey ?? conversationSource.sessionKey,
|
|
194
|
+
});
|
|
195
|
+
const kickoffBrief = resolveChatRequestKickoffBrief({
|
|
196
|
+
requestContext: normalizedContext,
|
|
197
|
+
source,
|
|
198
|
+
});
|
|
199
|
+
const openingPayload = normalizeChatRequestOpeningPayload(kickoffBrief?.payload ?? normalizedContext.openingPayload);
|
|
200
|
+
const broadcast = normalizeChatRequestBroadcast(normalizedContext.broadcast);
|
|
201
|
+
const followUp = normalizeChatRequestFollowUp(normalizedContext.followUp);
|
|
202
|
+
let origin = normalizeChatRequestOrigin(normalizedContext.origin, {
|
|
203
|
+
fallbackType: normalizeText(source, null) === 'world_broadcast' ? 'world_broadcast' : 'chat_request',
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
if (broadcast?.broadcastId && !origin?.broadcastId) {
|
|
207
|
+
origin = {
|
|
208
|
+
...(origin || { type: normalizeText(source, null) === 'world_broadcast' ? 'world_broadcast' : 'chat_request' }),
|
|
209
|
+
broadcastId: broadcast.broadcastId,
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
return {
|
|
214
|
+
kickoffBrief,
|
|
215
|
+
openingMessage: resolveChatRequestOpeningMessage({
|
|
216
|
+
openingMessage: kickoffBrief?.text ?? null,
|
|
217
|
+
openingPayload: kickoffBrief?.payload ?? openingPayload,
|
|
218
|
+
requestContext: normalizedContext,
|
|
219
|
+
}),
|
|
220
|
+
openingPayload: kickoffBrief?.payload ?? openingPayload,
|
|
221
|
+
conversation,
|
|
222
|
+
origin,
|
|
223
|
+
broadcast,
|
|
224
|
+
followUp,
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
export function buildChatRequestContext({
|
|
229
|
+
kickoffBrief = null,
|
|
230
|
+
openingMessage = null,
|
|
231
|
+
openingPayload = null,
|
|
232
|
+
conversation = {},
|
|
233
|
+
origin = null,
|
|
234
|
+
broadcast = null,
|
|
235
|
+
followUp = null,
|
|
236
|
+
source = 'chat_request',
|
|
237
|
+
} = {}) {
|
|
238
|
+
const normalizedConversation = normalizeChatRequestConversation(conversation);
|
|
239
|
+
const normalizedKickoffBrief = resolveChatRequestKickoffBrief({
|
|
240
|
+
kickoffBrief,
|
|
241
|
+
openingMessage,
|
|
242
|
+
openingPayload,
|
|
243
|
+
source,
|
|
244
|
+
});
|
|
245
|
+
const normalizedOpeningPayload = normalizeChatRequestOpeningPayload(
|
|
246
|
+
normalizedKickoffBrief?.payload ?? openingPayload,
|
|
247
|
+
);
|
|
248
|
+
const normalizedMessage = resolveChatRequestOpeningMessage({
|
|
249
|
+
openingMessage: normalizedKickoffBrief?.text ?? openingMessage,
|
|
250
|
+
openingPayload: normalizedOpeningPayload,
|
|
251
|
+
});
|
|
252
|
+
let normalizedOrigin = normalizeChatRequestOrigin(origin, {
|
|
253
|
+
fallbackType: normalizeText(source, null) === 'world_broadcast' ? 'world_broadcast' : 'chat_request',
|
|
254
|
+
});
|
|
255
|
+
let normalizedBroadcast = normalizeChatRequestBroadcast(broadcast);
|
|
256
|
+
const normalizedFollowUp = normalizeChatRequestFollowUp(followUp);
|
|
257
|
+
|
|
258
|
+
if (normalizedOrigin?.broadcastId && !normalizedBroadcast?.broadcastId) {
|
|
259
|
+
normalizedBroadcast = {
|
|
260
|
+
...(normalizedBroadcast || {}),
|
|
261
|
+
broadcastId: normalizedOrigin.broadcastId,
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
const requestContext = {
|
|
266
|
+
type: 'chat_request',
|
|
267
|
+
...(normalizedKickoffBrief ? { kickoffBrief: normalizedKickoffBrief } : {}),
|
|
268
|
+
...(normalizedMessage ? { message: normalizedMessage } : {}),
|
|
269
|
+
...(normalizedOpeningPayload ? { openingPayload: normalizedOpeningPayload } : {}),
|
|
270
|
+
...(Object.keys(normalizedConversation).length > 0 ? { conversation: normalizedConversation } : {}),
|
|
271
|
+
...(normalizedOrigin ? { origin: normalizedOrigin } : {}),
|
|
272
|
+
...(normalizedBroadcast ? { broadcast: normalizedBroadcast } : {}),
|
|
273
|
+
...(normalizedFollowUp ? { followUp: normalizedFollowUp } : {}),
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
return requestContext;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
export function normalizeStoredChatRequest(input = {}, { defaultSource = 'chat_request' } = {}) {
|
|
280
|
+
const normalizedSource = normalizeText(input.source, defaultSource);
|
|
281
|
+
const normalizedRequest = normalizeChatRequestInput({
|
|
282
|
+
requestContext: input.requestContext,
|
|
283
|
+
source: normalizedSource,
|
|
284
|
+
});
|
|
285
|
+
const kickoffBrief = resolveChatRequestKickoffBrief({
|
|
286
|
+
kickoffBrief: input.kickoffBrief,
|
|
287
|
+
openingMessage: input.openingMessage,
|
|
288
|
+
openingPayload: input.openingPayload ?? normalizedRequest.openingPayload,
|
|
289
|
+
requestContext: input.requestContext,
|
|
290
|
+
source: normalizedSource,
|
|
291
|
+
});
|
|
292
|
+
const openingPayload = normalizeChatRequestOpeningPayload(
|
|
293
|
+
input.openingPayload ?? kickoffBrief?.payload ?? normalizedRequest.openingPayload,
|
|
294
|
+
);
|
|
295
|
+
const openingMessage = resolveChatRequestOpeningMessage({
|
|
296
|
+
openingMessage: kickoffBrief?.text ?? input.openingMessage,
|
|
297
|
+
openingPayload,
|
|
298
|
+
requestContext: input.requestContext,
|
|
299
|
+
});
|
|
300
|
+
const conversation = normalizeChatRequestConversation(
|
|
301
|
+
input.conversation && typeof input.conversation === 'object' && !Array.isArray(input.conversation)
|
|
302
|
+
? input.conversation
|
|
303
|
+
: normalizedRequest.conversation,
|
|
304
|
+
);
|
|
305
|
+
const broadcast = normalizeChatRequestBroadcast(
|
|
306
|
+
input.broadcast && typeof input.broadcast === 'object' && !Array.isArray(input.broadcast)
|
|
307
|
+
? input.broadcast
|
|
308
|
+
: normalizedRequest.broadcast,
|
|
309
|
+
);
|
|
310
|
+
const followUp = normalizeChatRequestFollowUp(
|
|
311
|
+
input.followUp && typeof input.followUp === 'object' && !Array.isArray(input.followUp)
|
|
312
|
+
? input.followUp
|
|
313
|
+
: normalizedRequest.followUp,
|
|
314
|
+
);
|
|
315
|
+
let origin = normalizeChatRequestOrigin(
|
|
316
|
+
input.origin && typeof input.origin === 'object' && !Array.isArray(input.origin)
|
|
317
|
+
? input.origin
|
|
318
|
+
: normalizedRequest.origin,
|
|
319
|
+
{ fallbackType: normalizedSource === 'world_broadcast' ? 'world_broadcast' : 'chat_request' },
|
|
320
|
+
);
|
|
321
|
+
|
|
322
|
+
if (broadcast?.broadcastId && !origin?.broadcastId) {
|
|
323
|
+
origin = {
|
|
324
|
+
...(origin || { type: normalizedSource === 'world_broadcast' ? 'world_broadcast' : 'chat_request' }),
|
|
325
|
+
broadcastId: broadcast.broadcastId,
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
const chatRequestId = normalizeText(input.chatRequestId, normalizeText(input.requestId, null));
|
|
330
|
+
const normalized = {
|
|
331
|
+
chatRequestId,
|
|
332
|
+
requestId: chatRequestId,
|
|
333
|
+
fromAgentId: normalizeText(input.fromAgentId, null),
|
|
334
|
+
toAgentId: normalizeText(input.toAgentId, null),
|
|
335
|
+
openingMessage,
|
|
336
|
+
...(kickoffBrief ? { kickoffBrief } : {}),
|
|
337
|
+
...(openingPayload ? { openingPayload } : {}),
|
|
338
|
+
conversation,
|
|
339
|
+
...(origin ? { origin } : {}),
|
|
340
|
+
...(broadcast ? { broadcast } : {}),
|
|
341
|
+
requestContext: buildChatRequestContext({
|
|
342
|
+
kickoffBrief,
|
|
343
|
+
openingMessage,
|
|
344
|
+
openingPayload,
|
|
345
|
+
conversation,
|
|
346
|
+
origin,
|
|
347
|
+
broadcast,
|
|
348
|
+
followUp,
|
|
349
|
+
source: normalizedSource,
|
|
350
|
+
}),
|
|
351
|
+
status: normalizeText(input.status, 'pending'),
|
|
352
|
+
createdAt: normalizeText(input.createdAt, null),
|
|
353
|
+
respondedAt: normalizeText(input.respondedAt, null),
|
|
354
|
+
expiresAt: normalizeText(input.expiresAt, null),
|
|
355
|
+
source: normalizedSource,
|
|
356
|
+
};
|
|
357
|
+
|
|
358
|
+
const acceptedByAgentId = normalizeText(input.acceptedByAgentId, null);
|
|
359
|
+
if (acceptedByAgentId) normalized.acceptedByAgentId = acceptedByAgentId;
|
|
360
|
+
const approvalGrantId = normalizeText(input.approvalGrantId, null);
|
|
361
|
+
if (approvalGrantId) normalized.approvalGrantId = approvalGrantId;
|
|
362
|
+
const kickoff = normalizeAcceptedChatKickoffRecord(cloneJsonObject(input.kickoff));
|
|
363
|
+
if (kickoff) normalized.kickoff = kickoff;
|
|
364
|
+
|
|
365
|
+
return normalized;
|
|
366
|
+
}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import { randomBytes } from 'crypto';
|
|
2
|
+
|
|
3
|
+
export const PUBLIC_IDENTITY_STATUS = Object.freeze({
|
|
4
|
+
PENDING: 'pending',
|
|
5
|
+
READY: 'ready',
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
export const PUBLIC_IDENTITY_DISPLAY_NAME_MAX_LENGTH = 40;
|
|
9
|
+
export const PUBLIC_IDENTITY_CODE_LENGTH = 6;
|
|
10
|
+
const PUBLIC_IDENTITY_CODE_ALPHABET = '23456789ABCDEFGHJKLMNPQRSTUVWXYZ';
|
|
11
|
+
|
|
12
|
+
function normalizeText(value, fallback = null) {
|
|
13
|
+
if (value == null) return fallback;
|
|
14
|
+
const normalized = String(value).trim();
|
|
15
|
+
return normalized || fallback;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function normalizeIsoTimestamp(value, fallback = null) {
|
|
19
|
+
const normalized = normalizeText(value, null);
|
|
20
|
+
if (!normalized) return fallback;
|
|
21
|
+
const timestamp = Date.parse(normalized);
|
|
22
|
+
return Number.isFinite(timestamp) ? new Date(timestamp).toISOString() : fallback;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function cloneObject(value, fallback = {}) {
|
|
26
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) return { ...fallback };
|
|
27
|
+
return { ...value };
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function normalizePublicDisplayName(value, { fallback = null } = {}) {
|
|
31
|
+
const normalized = normalizeText(value, fallback);
|
|
32
|
+
if (!normalized) return fallback;
|
|
33
|
+
return normalized.slice(0, PUBLIC_IDENTITY_DISPLAY_NAME_MAX_LENGTH);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function validatePublicDisplayName(value) {
|
|
37
|
+
const normalized = normalizeText(value, null);
|
|
38
|
+
if (!normalized) {
|
|
39
|
+
return {
|
|
40
|
+
ok: false,
|
|
41
|
+
code: 'display_name_required',
|
|
42
|
+
message: 'displayName is required',
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
if (normalized.length > PUBLIC_IDENTITY_DISPLAY_NAME_MAX_LENGTH) {
|
|
46
|
+
return {
|
|
47
|
+
ok: false,
|
|
48
|
+
code: 'display_name_too_long',
|
|
49
|
+
message: `displayName must be ${PUBLIC_IDENTITY_DISPLAY_NAME_MAX_LENGTH} characters or fewer`,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
if (normalized.includes('#')) {
|
|
53
|
+
return {
|
|
54
|
+
ok: false,
|
|
55
|
+
code: 'display_name_reserved_character',
|
|
56
|
+
message: 'displayName must not include #',
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
if (/[\r\n\t]/.test(normalized)) {
|
|
60
|
+
return {
|
|
61
|
+
ok: false,
|
|
62
|
+
code: 'display_name_invalid_whitespace',
|
|
63
|
+
message: 'displayName must not include line breaks or tabs',
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
if (/[\u0000-\u001F\u007F]/.test(normalized)) {
|
|
67
|
+
return {
|
|
68
|
+
ok: false,
|
|
69
|
+
code: 'display_name_invalid_character',
|
|
70
|
+
message: 'displayName contains unsupported control characters',
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
return {
|
|
74
|
+
ok: true,
|
|
75
|
+
value: normalized,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function generatePublicIdentityCode({ length = PUBLIC_IDENTITY_CODE_LENGTH } = {}) {
|
|
80
|
+
const targetLength = Number.isInteger(length) && length > 0 ? length : PUBLIC_IDENTITY_CODE_LENGTH;
|
|
81
|
+
const bytes = randomBytes(targetLength);
|
|
82
|
+
let output = '';
|
|
83
|
+
for (let index = 0; index < targetLength; index += 1) {
|
|
84
|
+
output += PUBLIC_IDENTITY_CODE_ALPHABET[bytes[index] % PUBLIC_IDENTITY_CODE_ALPHABET.length];
|
|
85
|
+
}
|
|
86
|
+
return output;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export function formatPublicIdentityDisplay({ displayName = null, code = null } = {}) {
|
|
90
|
+
const normalizedDisplayName = normalizeText(displayName, null);
|
|
91
|
+
const normalizedCode = normalizeText(code, null);
|
|
92
|
+
if (!normalizedDisplayName || !normalizedCode) return null;
|
|
93
|
+
return `${normalizedDisplayName}#${normalizedCode}`;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export function parsePublicIdentityDisplay(value) {
|
|
97
|
+
const normalized = normalizeText(value, null);
|
|
98
|
+
if (!normalized) return null;
|
|
99
|
+
const hashIndex = normalized.lastIndexOf('#');
|
|
100
|
+
if (hashIndex <= 0 || hashIndex >= normalized.length - 1) return null;
|
|
101
|
+
const displayName = normalizeText(normalized.slice(0, hashIndex), null);
|
|
102
|
+
const code = normalizeText(normalized.slice(hashIndex + 1), null)?.toUpperCase() || null;
|
|
103
|
+
if (!displayName || !code) return null;
|
|
104
|
+
if (displayName.includes('#')) return null;
|
|
105
|
+
return {
|
|
106
|
+
displayName,
|
|
107
|
+
code,
|
|
108
|
+
identity: `${displayName}#${code}`,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export function buildPublicIdentityRecord(input = {}, {
|
|
113
|
+
fallbackDisplayName = null,
|
|
114
|
+
statusFallback = PUBLIC_IDENTITY_STATUS.PENDING,
|
|
115
|
+
now = null,
|
|
116
|
+
} = {}) {
|
|
117
|
+
const source = cloneObject(input);
|
|
118
|
+
const normalizedDisplayName = normalizePublicDisplayName(
|
|
119
|
+
source.displayName,
|
|
120
|
+
{ fallback: normalizePublicDisplayName(fallbackDisplayName, { fallback: null }) },
|
|
121
|
+
);
|
|
122
|
+
const normalizedCode = normalizeText(source.code, null)?.toUpperCase() || null;
|
|
123
|
+
const normalizedStatus = normalizeText(source.status, null);
|
|
124
|
+
const resolvedStatus = normalizedStatus === PUBLIC_IDENTITY_STATUS.READY
|
|
125
|
+
? PUBLIC_IDENTITY_STATUS.READY
|
|
126
|
+
: normalizedStatus === PUBLIC_IDENTITY_STATUS.PENDING
|
|
127
|
+
? PUBLIC_IDENTITY_STATUS.PENDING
|
|
128
|
+
: (normalizedCode && normalizedDisplayName ? PUBLIC_IDENTITY_STATUS.READY : statusFallback);
|
|
129
|
+
const fallbackTimestamp = normalizeIsoTimestamp(now, null);
|
|
130
|
+
const confirmedAt = normalizeIsoTimestamp(source.confirmedAt, null)
|
|
131
|
+
|| (resolvedStatus === PUBLIC_IDENTITY_STATUS.READY ? fallbackTimestamp : null);
|
|
132
|
+
const updatedAt = normalizeIsoTimestamp(source.updatedAt, fallbackTimestamp);
|
|
133
|
+
return {
|
|
134
|
+
displayName: normalizedDisplayName,
|
|
135
|
+
code: normalizedCode,
|
|
136
|
+
status: resolvedStatus,
|
|
137
|
+
confirmedAt,
|
|
138
|
+
updatedAt,
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export function resolvePublicIdentity(agent = {}) {
|
|
143
|
+
const publicIdentity = buildPublicIdentityRecord(agent?.publicIdentity, {
|
|
144
|
+
fallbackDisplayName: agent?.displayName || agent?.agentId || null,
|
|
145
|
+
now: agent?.createdAt || null,
|
|
146
|
+
});
|
|
147
|
+
return {
|
|
148
|
+
...publicIdentity,
|
|
149
|
+
displayIdentity: formatPublicIdentityDisplay(publicIdentity),
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export function isPublicIdentityReady(agent = {}) {
|
|
154
|
+
return resolvePublicIdentity(agent).status === PUBLIC_IDENTITY_STATUS.READY;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export function buildPublicIdentityMissingFields(agent = {}) {
|
|
158
|
+
const publicIdentity = resolvePublicIdentity(agent);
|
|
159
|
+
const missingFields = [];
|
|
160
|
+
if (!publicIdentity.displayName) {
|
|
161
|
+
missingFields.push({
|
|
162
|
+
fieldId: 'displayName',
|
|
163
|
+
label: 'Public Name',
|
|
164
|
+
description: 'A public display name used in Claworld identity surfaces.',
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
if (!publicIdentity.code) {
|
|
168
|
+
missingFields.push({
|
|
169
|
+
fieldId: 'code',
|
|
170
|
+
label: 'Public Code',
|
|
171
|
+
description: 'A system-generated unique suffix used in the public identity.',
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
return missingFields;
|
|
175
|
+
}
|