@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.
Files changed (48) hide show
  1. package/README.md +3 -3
  2. package/index.js +50 -0
  3. package/openclaw.plugin.json +1 -1
  4. package/package.json +4 -1
  5. package/setup-entry.js +6 -0
  6. package/skills/claworld-a2a-channel-agent/SKILL.md +218 -0
  7. package/skills/claworld-help/SKILL.md +304 -0
  8. package/skills/claworld-join-and-chat/SKILL.md +526 -0
  9. package/skills/claworld-manage-worlds/SKILL.md +292 -0
  10. package/skills/claworld-manage-worlds/references/world-context-templates.md +145 -0
  11. package/src/lib/chat-request.js +366 -0
  12. package/src/lib/public-identity.js +175 -0
  13. package/src/lib/relay/agent-readable-markdown.js +385 -0
  14. package/src/lib/relay/kickoff-progress.js +162 -0
  15. package/src/lib/relay/kickoff-text.js +191 -0
  16. package/src/lib/relay/shared.js +30 -0
  17. package/src/lib/runtime-errors.js +149 -0
  18. package/src/openclaw/index.js +51 -0
  19. package/src/openclaw/plugin/account-identity.js +73 -0
  20. package/src/openclaw/plugin/claworld-channel-plugin.js +3499 -0
  21. package/src/openclaw/plugin/config-schema.js +392 -0
  22. package/src/openclaw/plugin/lifecycle.js +114 -0
  23. package/src/openclaw/plugin/managed-config.js +1054 -0
  24. package/src/openclaw/plugin/onboarding.js +312 -0
  25. package/src/openclaw/plugin/register-tooling.js +728 -0
  26. package/src/openclaw/plugin/register.js +1616 -0
  27. package/src/openclaw/plugin/relay-client-shared.js +146 -0
  28. package/src/openclaw/plugin/relay-client.js +1469 -0
  29. package/src/openclaw/plugin/runtime-backup.js +105 -0
  30. package/src/openclaw/plugin/runtime.js +12 -0
  31. package/src/openclaw/plugin-version.js +67 -0
  32. package/src/openclaw/protocol/relay-event-protocol.js +43 -0
  33. package/src/openclaw/runtime/backend-error-context.js +91 -0
  34. package/src/openclaw/runtime/canonical-result-builder.js +126 -0
  35. package/src/openclaw/runtime/demo-session-bootstrap.js +32 -0
  36. package/src/openclaw/runtime/feedback-helper.js +145 -0
  37. package/src/openclaw/runtime/inbound-session-router.js +44 -0
  38. package/src/openclaw/runtime/outbound-session-bridge.js +29 -0
  39. package/src/openclaw/runtime/product-shell-helper.js +931 -0
  40. package/src/openclaw/runtime/runtime-path.js +19 -0
  41. package/src/openclaw/runtime/system-message-orchestrator.js +1 -0
  42. package/src/openclaw/runtime/tool-contracts.js +939 -0
  43. package/src/openclaw/runtime/tool-inventory.js +83 -0
  44. package/src/openclaw/runtime/world-membership-helper.js +320 -0
  45. package/src/openclaw/runtime/world-moderation-helper.js +508 -0
  46. package/src/product-shell/contracts/chat-request-approval-policy.js +93 -0
  47. package/src/product-shell/contracts/world-orchestration.js +734 -0
  48. 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
+ }