@xfxstudio/claworld 0.2.15 → 0.2.16

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.
@@ -1,10 +1,7 @@
1
1
  import { createClaworldChannelPlugin } from './claworld-channel-plugin.js';
2
2
  import {
3
- projectToolChatInboxResponse,
4
3
  projectToolChatRequestMutationResponse,
5
4
  projectToolCreateWorldResponse,
6
- projectToolManagedWorldResponse,
7
- projectToolOwnedWorldsResponse,
8
5
  projectToolWorldList,
9
6
  projectToolFeedbackSubmissionResponse,
10
7
  projectToolJoinWorldResponse,
@@ -12,83 +9,37 @@ import {
12
9
  } from '../runtime/tool-contracts.js';
13
10
  import { CLAWORLD_TOOL_CONTRACT_VERSION } from '../runtime/tool-inventory.js';
14
11
  import { setClaworldRuntime } from './runtime.js';
15
- import {
16
- buildPublicErrorPayload,
17
- createRuntimeBoundaryError,
18
- logRuntimeBoundary,
19
- normalizeRuntimeBoundaryError,
20
- } from '../../lib/runtime-errors.js';
21
- import {
22
- normalizeBackendFieldError,
23
- normalizeBackendMissingField,
24
- normalizeBackendPublicIdentity,
25
- } from '../runtime/backend-error-context.js';
26
12
  import {
27
13
  CHAT_REQUEST_APPROVAL_POLICY_MODES,
28
14
  CHAT_REQUEST_APPROVAL_POLICY_ORIGIN_TYPES,
29
15
  } from '../../product-shell/contracts/chat-request-approval-policy.js';
30
-
31
- const INTERNAL_REQUESTER_SESSION_KEY_PARAM = '__claworldRequesterSessionKey';
32
-
33
- function normalizeText(value, fallback = null) {
34
- if (value == null) return fallback;
35
- const normalized = String(value).trim();
36
- return normalized || fallback;
37
- }
38
-
39
- function normalizeObject(value, fallback = null) {
40
- if (!value || typeof value !== 'object' || Array.isArray(value)) return fallback;
41
- return value;
42
- }
43
-
44
- function normalizePublicFieldError(fieldError = {}) {
45
- const fieldId = normalizeText(fieldError.fieldId, null);
46
- const message = normalizeText(fieldError.message, null);
47
- const code = normalizeText(fieldError.code, null);
48
- if (!fieldId && !message && !code) return null;
49
- return {
50
- ...(fieldId ? { fieldId } : {}),
51
- ...(message ? { message } : {}),
52
- ...(code ? { code } : {}),
53
- };
54
- }
55
-
56
- function buildPublicToolErrorExtras(error) {
57
- const context = normalizeObject(error?.context, null);
58
- if (!context) return null;
59
-
60
- const httpStatus = Number(context.httpStatus);
61
- const backendCode = normalizeText(context.backendCode, null);
62
- const backendMessage = normalizeText(context.backendMessage, null);
63
- const fieldErrors = Array.isArray(context.fieldErrors)
64
- ? context.fieldErrors
65
- .map((fieldError) => normalizeBackendFieldError(fieldError) || normalizePublicFieldError(fieldError))
66
- .filter(Boolean)
67
- : [];
68
- const requiredAction = normalizeText(context.requiredAction, null);
69
- const nextAction = normalizeText(context.nextAction, null);
70
- const nextTool = normalizeText(context.nextTool, null);
71
- const missingFields = Array.isArray(context.missingFields)
72
- ? context.missingFields
73
- .map((field) => normalizeBackendMissingField(field))
74
- .filter(Boolean)
75
- : [];
76
- const publicIdentity = normalizeBackendPublicIdentity(context.publicIdentity);
77
-
78
- const extra = {
79
- ...(Number.isInteger(httpStatus) && httpStatus > 0 ? { httpStatus } : {}),
80
- ...(backendCode ? { backendCode } : {}),
81
- ...(backendMessage ? { backendMessage } : {}),
82
- ...(fieldErrors.length > 0 ? { fieldErrors } : {}),
83
- ...(requiredAction ? { requiredAction } : {}),
84
- ...(nextAction ? { nextAction } : {}),
85
- ...(nextTool ? { nextTool } : {}),
86
- ...(missingFields.length > 0 ? { missingFields } : {}),
87
- ...(publicIdentity ? { publicIdentity } : {}),
88
- };
89
-
90
- return Object.keys(extra).length > 0 ? extra : null;
91
- }
16
+ import {
17
+ ACCOUNT_ACTIONS,
18
+ arrayParam,
19
+ booleanParam,
20
+ buildToolMetadata,
21
+ buildToolResult,
22
+ CHAT_INBOX_ACTIONS,
23
+ inferAccountAction,
24
+ inferChatInboxAction,
25
+ inferManageWorldAction,
26
+ INTERNAL_REQUESTER_SESSION_KEY_PARAM,
27
+ integerParam,
28
+ loadCurrentConfig,
29
+ MANAGE_WORLD_ACTIONS,
30
+ normalizeManageWorldAction,
31
+ normalizeObject,
32
+ normalizeText,
33
+ objectParam,
34
+ projectToolAccountMutationResponse,
35
+ projectToolAccountViewResponse,
36
+ projectToolChatInboxActionResponse,
37
+ projectToolManageWorldActionResponse,
38
+ requireManageWorldField,
39
+ resolveToolContext,
40
+ stringParam,
41
+ withToolErrorBoundary,
42
+ } from './register-tooling.js';
92
43
 
93
44
  function buildClaworldStatusRoute(plugin) {
94
45
  return {
@@ -118,471 +69,6 @@ function buildClaworldStatusRoute(plugin) {
118
69
  };
119
70
  }
120
71
 
121
- async function loadCurrentConfig(api) {
122
- if (api?.config && typeof api.config.loadConfig === 'function') {
123
- return await api.config.loadConfig();
124
- }
125
- if (api?.runtime?.config && typeof api.runtime.config.loadConfig === 'function') {
126
- return await api.runtime.config.loadConfig();
127
- }
128
- if (api?.config && typeof api.config === 'object') {
129
- return api.config;
130
- }
131
- return {};
132
- }
133
-
134
- function buildToolResult(payload) {
135
- return {
136
- content: [
137
- {
138
- type: 'text',
139
- text: JSON.stringify(payload, null, 2),
140
- },
141
- ],
142
- };
143
- }
144
-
145
- function buildToolErrorResult(toolName, error) {
146
- const normalized = normalizeRuntimeBoundaryError(error, {
147
- code: 'claworld_tool_execution_failed',
148
- category: 'runtime',
149
- publicMessage: 'tool execution failed',
150
- recoverable: true,
151
- });
152
- return buildToolResult({
153
- status: 'error',
154
- tool: toolName,
155
- ...buildPublicErrorPayload(normalized, {
156
- errorType: 'claworld_tool_failed',
157
- fallbackMessage: 'tool execution failed',
158
- exposeMessage: normalized.status < 500 || Boolean(normalized.publicMessage),
159
- extra: buildPublicToolErrorExtras(normalized),
160
- }),
161
- });
162
- }
163
-
164
- function withToolErrorBoundary(toolName, execute) {
165
- return async (toolCallId, params = {}) => {
166
- try {
167
- return await execute(toolCallId, params);
168
- } catch (error) {
169
- const normalized = logRuntimeBoundary(console, `[claworld:tool:${toolName}] execution failed`, error, {
170
- tool: toolName,
171
- }, {
172
- includeStack: false,
173
- fallback: {
174
- code: 'claworld_tool_execution_failed',
175
- category: 'runtime',
176
- publicMessage: 'tool execution failed',
177
- recoverable: true,
178
- },
179
- });
180
- return buildToolErrorResult(toolName, normalized);
181
- }
182
- };
183
- }
184
-
185
- async function resolveToolContext(api, plugin, params = {}, { bindRuntime = true } = {}) {
186
- const cfg = await loadCurrentConfig(api);
187
- const accountId = normalizeText(params.accountId, plugin.config.defaultAccountId(cfg) || null);
188
- const runtimeConfig = plugin.config.resolveRuntimeConfig(cfg, accountId);
189
-
190
- if (bindRuntime && typeof plugin.helpers?.resolveToolRuntimeContext === 'function') {
191
- return await plugin.helpers.resolveToolRuntimeContext({
192
- cfg,
193
- accountId,
194
- runtimeConfig,
195
- agentId: normalizeText(params.agentId, runtimeConfig.relay?.agentId || null),
196
- requesterSessionKey: normalizeText(params[INTERNAL_REQUESTER_SESSION_KEY_PARAM], null),
197
- });
198
- }
199
-
200
- const agentId = normalizeText(params.agentId, runtimeConfig.relay?.agentId || null);
201
- return {
202
- cfg,
203
- accountId,
204
- runtimeConfig,
205
- agentId,
206
- requesterSessionKey: normalizeText(params[INTERNAL_REQUESTER_SESSION_KEY_PARAM], null),
207
- };
208
- }
209
-
210
- function cloneMetadataValue(value) {
211
- return value == null ? value : JSON.parse(JSON.stringify(value));
212
- }
213
-
214
- function stringParam({
215
- description = null,
216
- minLength = null,
217
- enumValues = null,
218
- pattern = null,
219
- examples = [],
220
- } = {}) {
221
- return {
222
- type: 'string',
223
- ...(description ? { description } : {}),
224
- ...(Number.isInteger(minLength) && minLength > 0 ? { minLength } : {}),
225
- ...(Array.isArray(enumValues) && enumValues.length > 0 ? { enum: enumValues } : {}),
226
- ...(pattern ? { pattern } : {}),
227
- ...(Array.isArray(examples) && examples.length > 0 ? { examples } : {}),
228
- };
229
- }
230
-
231
- function integerParam({
232
- description = null,
233
- minimum = null,
234
- maximum = null,
235
- examples = [],
236
- } = {}) {
237
- return {
238
- type: 'integer',
239
- ...(description ? { description } : {}),
240
- ...(Number.isInteger(minimum) ? { minimum } : {}),
241
- ...(Number.isInteger(maximum) ? { maximum } : {}),
242
- ...(Array.isArray(examples) && examples.length > 0 ? { examples } : {}),
243
- };
244
- }
245
-
246
- function booleanParam({
247
- description = null,
248
- defaultValue = null,
249
- } = {}) {
250
- return {
251
- type: 'boolean',
252
- ...(description ? { description } : {}),
253
- ...(typeof defaultValue === 'boolean' ? { default: defaultValue } : {}),
254
- };
255
- }
256
-
257
- function objectParam({
258
- description = null,
259
- properties = {},
260
- required = [],
261
- additionalProperties = false,
262
- examples = [],
263
- } = {}) {
264
- return {
265
- type: 'object',
266
- additionalProperties,
267
- ...(description ? { description } : {}),
268
- ...(Array.isArray(required) && required.length > 0 ? { required } : {}),
269
- properties,
270
- ...(Array.isArray(examples) && examples.length > 0 ? { examples: examples.map(cloneMetadataValue) } : {}),
271
- };
272
- }
273
-
274
- function arrayParam({
275
- description = null,
276
- items = {},
277
- maxItems = null,
278
- examples = [],
279
- } = {}) {
280
- return {
281
- type: 'array',
282
- items,
283
- ...(description ? { description } : {}),
284
- ...(Number.isInteger(maxItems) ? { maxItems } : {}),
285
- ...(Array.isArray(examples) && examples.length > 0 ? { examples: examples.map(cloneMetadataValue) } : {}),
286
- };
287
- }
288
-
289
- function buildToolMetadata({
290
- category,
291
- usageNotes = [],
292
- examples = [],
293
- } = {}) {
294
- return {
295
- surface: 'canonical_public',
296
- canonical: true,
297
- category: normalizeText(category, 'general'),
298
- usageNotes: Array.isArray(usageNotes) ? usageNotes.filter(Boolean) : [],
299
- examples: Array.isArray(examples)
300
- ? examples.map((example) => cloneMetadataValue(example)).filter(Boolean)
301
- : [],
302
- };
303
- }
304
-
305
- const MANAGE_WORLD_ACTIONS = Object.freeze([
306
- 'list',
307
- 'get',
308
- 'update_context',
309
- 'pause',
310
- 'close',
311
- 'resume',
312
- ]);
313
-
314
- function normalizeManageWorldAction(value, fallback = null) {
315
- const normalized = normalizeText(value, fallback);
316
- return MANAGE_WORLD_ACTIONS.includes(normalized) ? normalized : fallback;
317
- }
318
-
319
- function inferManageWorldAction(params = {}) {
320
- const explicitAction = normalizeManageWorldAction(params.action, null);
321
- if (explicitAction) return explicitAction;
322
- if (!normalizeText(params.worldId, null)) return 'list';
323
- if (normalizeText(params.worldContextText, null) || normalizeText(params.displayName, null)) {
324
- return 'update_context';
325
- }
326
- return 'get';
327
- }
328
-
329
- function requireManageWorldField(fieldId, message = `${fieldId} is required`) {
330
- throw createRuntimeBoundaryError({
331
- code: 'tool_input_invalid',
332
- category: 'input',
333
- status: 400,
334
- message,
335
- publicMessage: message,
336
- recoverable: true,
337
- context: { field: fieldId },
338
- });
339
- }
340
-
341
- function projectToolManageWorldActionResponse(payload = {}, { accountId = null, action = null } = {}) {
342
- const resolvedAction = normalizeManageWorldAction(action, null) || 'get';
343
- if (resolvedAction === 'list') {
344
- return {
345
- action: resolvedAction,
346
- ...projectToolOwnedWorldsResponse(payload, { accountId }),
347
- };
348
- }
349
- return {
350
- action: resolvedAction,
351
- ...projectToolManagedWorldResponse(payload, { accountId }),
352
- };
353
- }
354
-
355
- const CHAT_INBOX_ACTIONS = Object.freeze([
356
- 'list',
357
- 'accept',
358
- 'reject',
359
- ]);
360
-
361
- function normalizeChatInboxAction(value, fallback = null) {
362
- const normalized = normalizeText(value, fallback);
363
- return CHAT_INBOX_ACTIONS.includes(normalized) ? normalized : fallback;
364
- }
365
-
366
- function inferChatInboxAction(params = {}) {
367
- return normalizeChatInboxAction(params.action, 'list');
368
- }
369
-
370
- function projectToolChatInboxActionResponse(payload = {}, { accountId = null, action = 'list' } = {}) {
371
- const resolvedAction = normalizeChatInboxAction(action, 'list');
372
- if (resolvedAction === 'list') {
373
- return {
374
- action: resolvedAction,
375
- ...projectToolChatInboxResponse(payload, { accountId }),
376
- };
377
- }
378
- return {
379
- action: resolvedAction,
380
- ...projectToolChatRequestMutationResponse(payload, { accountId }),
381
- };
382
- }
383
-
384
- const ACCOUNT_ACTIONS = Object.freeze([
385
- 'view',
386
- 'update_identity',
387
- 'update_chat_policy',
388
- ]);
389
-
390
- function normalizeAccountAction(value, fallback = null) {
391
- const normalized = normalizeText(value, fallback);
392
- return ACCOUNT_ACTIONS.includes(normalized) ? normalized : fallback;
393
- }
394
-
395
- function inferAccountAction(params = {}) {
396
- const explicitAction = normalizeAccountAction(params.action, null);
397
- if (explicitAction) return explicitAction;
398
- if (normalizeText(params.displayName, null)) return 'update_identity';
399
- if (normalizeObject(params.chatRequestApprovalPolicy, null)) return 'update_chat_policy';
400
- return 'view';
401
- }
402
-
403
- function projectToolPublicIdentity(payload = null) {
404
- if (!payload || typeof payload !== 'object') return null;
405
- return {
406
- status: payload.status || null,
407
- ready: payload.ready ?? null,
408
- publicIdentity: payload.publicIdentity && typeof payload.publicIdentity === 'object'
409
- ? {
410
- status: payload.publicIdentity.status || null,
411
- displayIdentity: payload.publicIdentity.displayIdentity || null,
412
- displayName: payload.publicIdentity.displayName || null,
413
- code: payload.publicIdentity.code || null,
414
- confirmedAt: payload.publicIdentity.confirmedAt || null,
415
- updatedAt: payload.publicIdentity.updatedAt || null,
416
- }
417
- : null,
418
- recommendedDisplayName: payload.recommendedDisplayName || null,
419
- requiredAction: payload.requiredAction || null,
420
- nextAction: payload.nextAction || null,
421
- nextTool: payload.nextTool || null,
422
- missingFields: Array.isArray(payload.missingFields) ? payload.missingFields : [],
423
- feedbackSummary: payload.feedbackSummary && typeof payload.feedbackSummary === 'object'
424
- ? {
425
- totalLikesReceived: Number(payload.feedbackSummary.totalLikesReceived || 0),
426
- totalDislikesReceived: Number(payload.feedbackSummary.totalDislikesReceived || 0),
427
- totalLikesGiven: Number(payload.feedbackSummary.totalLikesGiven || 0),
428
- totalDislikesGiven: Number(payload.feedbackSummary.totalDislikesGiven || 0),
429
- }
430
- : null,
431
- };
432
- }
433
-
434
- function projectToolShareCard(payload = null) {
435
- const card = payload?.card && typeof payload.card === 'object' ? payload.card : null;
436
- const imageUrl = normalizeText(card?.imageUrl, normalizeText(payload?.imageUrl, null));
437
- const downloadUrl = normalizeText(card?.downloadUrl, normalizeText(payload?.downloadUrl, imageUrl));
438
- const templateId = normalizeText(card?.templateId, normalizeText(payload?.templateId, null));
439
- const expiresAt = normalizeText(card?.expiresAt, normalizeText(payload?.expiresAt, null));
440
- const description = normalizeText(card?.description, normalizeText(payload?.description, null));
441
- if (!imageUrl && !downloadUrl && !templateId && !expiresAt && !description) {
442
- return {
443
- status: normalizeText(payload?.status, 'unavailable'),
444
- reason: normalizeText(payload?.reason, null),
445
- message: normalizeText(payload?.message, null),
446
- };
447
- }
448
- return {
449
- status: normalizeText(payload?.status, 'ready'),
450
- imageUrl,
451
- downloadUrl,
452
- templateId,
453
- expiresAt,
454
- description,
455
- };
456
- }
457
-
458
- function projectToolAccountIdentityFields(identityPayload = null) {
459
- const projectedIdentity = projectToolPublicIdentity(identityPayload);
460
- if (projectedIdentity) {
461
- return {
462
- publicIdentity: projectedIdentity.publicIdentity,
463
- recommendedDisplayName: projectedIdentity.recommendedDisplayName,
464
- requiredAction: projectedIdentity.requiredAction,
465
- nextAction: projectedIdentity.nextAction,
466
- nextTool: projectedIdentity.nextTool,
467
- missingFields: projectedIdentity.missingFields,
468
- feedbackSummary: projectedIdentity.feedbackSummary,
469
- };
470
- }
471
- return {
472
- publicIdentity: null,
473
- recommendedDisplayName: null,
474
- requiredAction: null,
475
- nextAction: null,
476
- nextTool: null,
477
- missingFields: [],
478
- feedbackSummary: null,
479
- };
480
- }
481
-
482
- function projectToolChatRequestApprovalPolicy(payload = null) {
483
- const policy = normalizeObject(payload, null);
484
- if (!policy) return null;
485
- return {
486
- agentId: normalizeText(policy.agentId, null),
487
- schemaVersion: Number.isInteger(policy.schemaVersion) ? policy.schemaVersion : null,
488
- syncedAt: normalizeText(policy.syncedAt, null),
489
- credentialId: normalizeText(policy.credentialId, null),
490
- source: normalizeObject(policy.source, {})
491
- ? {
492
- channel: normalizeText(policy.source?.channel, null),
493
- integration: normalizeText(policy.source?.integration, null),
494
- accountId: normalizeText(policy.source?.accountId, null),
495
- }
496
- : {},
497
- policy: normalizeObject(policy.policy, {})
498
- ? {
499
- mode: normalizeText(policy.policy?.mode, null),
500
- blocks: {
501
- originTypes: Array.isArray(policy.policy?.blocks?.originTypes) ? policy.policy.blocks.originTypes : [],
502
- worldIds: Array.isArray(policy.policy?.blocks?.worldIds) ? policy.policy.blocks.worldIds : [],
503
- },
504
- }
505
- : null,
506
- };
507
- }
508
-
509
- function projectToolAccountViewResponse({
510
- accountId = null,
511
- pairingPayload = null,
512
- identityPayload = null,
513
- } = {}) {
514
- const identityReady = identityPayload?.ready === true;
515
- const ready = pairingPayload?.status === 'paired' && identityReady;
516
- const resolvedShareCard = identityPayload && Object.prototype.hasOwnProperty.call(identityPayload, 'shareCard')
517
- ? projectToolShareCard(identityPayload.shareCard)
518
- : undefined;
519
- return {
520
- action: 'view',
521
- status: ready ? 'ready' : 'pending',
522
- ready,
523
- readiness: pairingPayload?.status === 'paired'
524
- ? (identityReady ? 'paired_and_ready' : 'paired_but_identity_pending')
525
- : 'installed_unactivated',
526
- accountId: normalizeText(pairingPayload?.runtimeConfig?.accountId, normalizeText(accountId, null)),
527
- reason: normalizeText(pairingPayload?.reason, null),
528
- bindingSource: normalizeText(pairingPayload?.bindingSource, null),
529
- activation: {
530
- status: pairingPayload?.status === 'paired' ? 'ready' : 'pending',
531
- },
532
- relay: {
533
- agentId: normalizeText(
534
- pairingPayload?.runtimeConfig?.relay?.agentId,
535
- normalizeText(pairingPayload?.relayAgent?.agentId, null),
536
- ),
537
- displayName: normalizeText(pairingPayload?.relayAgent?.displayName, null),
538
- discoverable: pairingPayload?.relayAgent?.discoverable ?? null,
539
- contactable: pairingPayload?.relayAgent?.contactable ?? null,
540
- online: pairingPayload?.relayAgent?.online ?? null,
541
- resolved: pairingPayload?.relayAgent?.resolved ?? null,
542
- bindingStatus: pairingPayload?.status === 'paired' ? 'bound' : 'unactivated',
543
- },
544
- ...projectToolAccountIdentityFields(identityPayload),
545
- chatRequestApprovalPolicy: projectToolChatRequestApprovalPolicy(identityPayload?.chatRequestApprovalPolicy),
546
- ...(resolvedShareCard !== undefined ? { shareCard: resolvedShareCard } : {}),
547
- };
548
- }
549
-
550
- function projectToolAccountMutationResponse({
551
- action = 'update_identity',
552
- accountId = null,
553
- identityPayload = null,
554
- shareCard = undefined,
555
- runtimeActivation = null,
556
- } = {}) {
557
- const resolvedShareCard = shareCard !== undefined
558
- ? shareCard
559
- : (identityPayload && Object.prototype.hasOwnProperty.call(identityPayload, 'shareCard')
560
- ? projectToolShareCard(identityPayload.shareCard)
561
- : undefined);
562
- const ready = identityPayload?.ready === true;
563
- return {
564
- action,
565
- status: ready ? 'ready' : 'pending',
566
- ready,
567
- accountId: normalizeText(accountId, null),
568
- ...projectToolAccountIdentityFields(identityPayload),
569
- chatRequestApprovalPolicy: projectToolChatRequestApprovalPolicy(identityPayload?.chatRequestApprovalPolicy),
570
- ...(resolvedShareCard !== undefined ? { shareCard: resolvedShareCard } : {}),
571
- ...(runtimeActivation ? { runtimeActivation } : {}),
572
- ...(action === 'update_identity'
573
- ? {
574
- updated: resolvedShareCard && resolvedShareCard.status === 'ready'
575
- ? ['publicIdentity', 'shareCard']
576
- : ['publicIdentity'],
577
- }
578
- : action === 'update_chat_policy'
579
- ? {
580
- updated: ['chatRequestApprovalPolicy'],
581
- }
582
- : {}),
583
- };
584
- }
585
-
586
72
  function buildRegisteredTools(api, plugin) {
587
73
  const accountIdProperty = stringParam({
588
74
  description: 'Claworld account id to execute the tool against. In managed installs this is usually the dedicated claworld account.',
@@ -807,7 +293,7 @@ function buildRegisteredTools(api, plugin) {
807
293
  category: 'world_creation',
808
294
  usageNotes: [
809
295
  'Use only when the user explicitly wants to create a new owner-managed world.',
810
- 'Provide displayName plus worldContextText.',
296
+ 'Provide displayName plus worldContextText; the backend issues the canonical worldId.',
811
297
  'Follow-up management tools read back owner/worldContext/status and the participantContextField description.',
812
298
  ],
813
299
  examples: [
@@ -818,7 +304,7 @@ function buildRegisteredTools(api, plugin) {
818
304
  displayName: 'Weekend Debate Club',
819
305
  worldContextText: '世界:Weekend Debate Club\n简介:A creator-managed world for short structured debates.\n互动规则:Debate one topic at a time and stay concise.',
820
306
  },
821
- outcome: 'Creates one draft owner-managed world and returns the canonical world contract.',
307
+ outcome: 'Creates one owner-managed world and returns the canonical contract with a backend-issued worldId.',
822
308
  },
823
309
  ],
824
310
  }),
@@ -888,15 +374,15 @@ function buildRegisteredTools(api, plugin) {
888
374
  input: {
889
375
  accountId: 'claworld',
890
376
  action: 'update_context',
891
- worldId: 'ugc-weekend-debate-club',
892
- worldContextText: '世界:Weekend Debate Club [ugc-weekend-debate-club]\n简介:A creator-managed world for short structured debates.\n互动规则:Debate one topic at a time and stay concise.',
377
+ worldId: 'wld_7bd61af2-d9d3-47fb-8bc7-632843e1d0fd',
378
+ worldContextText: '世界:Weekend Debate Club\n简介:A creator-managed world for short structured debates.\n互动规则:Debate one topic at a time and stay concise.',
893
379
  },
894
380
  outcome: 'Returns the updated managed-world projection when the current agent is the owner.',
895
381
  },
896
382
  ],
897
383
  }),
898
384
  parameters: objectParam({
899
- description: 'Owner-only world governance payload. action=list replaces the old list_owned_worlds tool.',
385
+ description: 'Owner-only world governance payload.',
900
386
  required: ['accountId'],
901
387
  properties: {
902
388
  accountId: accountIdProperty,
@@ -909,7 +395,7 @@ function buildRegisteredTools(api, plugin) {
909
395
  worldContextText: stringParam({
910
396
  description: 'Replacement canonical world context text for update_context.',
911
397
  minLength: 1,
912
- examples: ['世界:Weekend Debate Club [ugc-weekend-debate-club]\n简介:A creator-managed world for short structured debates.\n互动规则:Debate one topic at a time and stay concise.'],
398
+ examples: ['世界:Weekend Debate Club\n简介:A creator-managed world for short structured debates.\n互动规则:Debate one topic at a time and stay concise.'],
913
399
  }),
914
400
  displayName: stringParam({
915
401
  description: 'Optional new display name when action=update_context.',
@@ -929,8 +415,8 @@ function buildRegisteredTools(api, plugin) {
929
415
  {
930
416
  accountId: 'claworld',
931
417
  action: 'update_context',
932
- worldId: 'ugc-weekend-debate-club',
933
- worldContextText: '世界:Weekend Debate Club [ugc-weekend-debate-club]\n简介:A creator-managed world for short structured debates.\n互动规则:Debate one topic at a time and stay concise.',
418
+ worldId: 'wld_7bd61af2-d9d3-47fb-8bc7-632843e1d0fd',
419
+ worldContextText: '世界:Weekend Debate Club\n简介:A creator-managed world for short structured debates.\n互动规则:Debate one topic at a time and stay concise.',
934
420
  },
935
421
  ],
936
422
  }),
@@ -1007,11 +493,13 @@ function buildRegisteredTools(api, plugin) {
1007
493
  {
1008
494
  name: 'claworld_request_chat',
1009
495
  label: 'Claworld Request Chat',
1010
- description: 'Canonical conversation-start tool. Use the targetAgentId returned by claworld_join_world candidate delivery or another canonical Claworld identity surface.',
496
+ description: 'Canonical conversation-start tool. Use the target displayName and agentCode returned by claworld_join_world candidate delivery.',
1011
497
  metadata: buildToolMetadata({
1012
498
  category: 'chat_request',
1013
499
  usageNotes: [
1014
- 'For world-scoped chat, use the targetAgentId returned by claworld_join_world.',
500
+ 'For world-scoped chat, use the displayName and agentCode returned by claworld_join_world candidate delivery.',
501
+ 'The backend resolves the target by agentCode.',
502
+ 'If the current displayName for that agentCode no longer matches, the tool returns an explicit conflict.',
1015
503
  'After creation, use claworld_chat_inbox or wait for the peer to accept.',
1016
504
  'Once accepted, the runtime owns the live conversation loop.',
1017
505
  ],
@@ -1021,22 +509,38 @@ function buildRegisteredTools(api, plugin) {
1021
509
  input: {
1022
510
  accountId: 'claworld',
1023
511
  worldId: 'dating-demo-world',
1024
- targetAgentId: 'agt_runtime_candidate',
512
+ displayName: 'Runtime Candidate',
513
+ agentCode: 'ZX82QP',
1025
514
  openingMessage: 'Hi, want to compare trail-running routes in Shanghai?',
1026
515
  },
1027
516
  outcome: 'Creates one pending world-scoped chat request.',
1028
517
  },
518
+ {
519
+ title: 'Request chat by public identity',
520
+ input: {
521
+ accountId: 'claworld',
522
+ displayName: 'Runtime Candidate',
523
+ agentCode: 'ZX82QP',
524
+ openingMessage: 'Hi, want to compare trail-running routes in Shanghai?',
525
+ },
526
+ outcome: 'Creates one pending direct chat request.',
527
+ },
1029
528
  ],
1030
529
  }),
1031
530
  parameters: objectParam({
1032
- description: 'Create a direct or world-scoped chat request for one target agent.',
1033
- required: ['accountId', 'targetAgentId'],
531
+ description: 'Create a direct or world-scoped chat request for one target agent. Provide the target displayName and agentCode.',
532
+ required: ['accountId', 'displayName', 'agentCode'],
1034
533
  properties: {
1035
534
  accountId: accountIdProperty,
1036
- targetAgentId: stringParam({
1037
- description: 'Canonical target agentId. For world flows, use the targetAgentId returned by claworld_join_world candidate delivery.',
535
+ displayName: stringParam({
536
+ description: 'Target public displayName.',
1038
537
  minLength: 1,
1039
- examples: ['agt_runtime_candidate'],
538
+ examples: ['Runtime Candidate'],
539
+ }),
540
+ agentCode: stringParam({
541
+ description: 'Target public agentCode. The backend resolves the target by this code and verifies the displayName still matches.',
542
+ minLength: 1,
543
+ examples: ['ZX82QP'],
1040
544
  }),
1041
545
  openingMessage: stringParam({
1042
546
  description: 'Kickoff brief or opener intent that the backend uses when the peer accepts.',
@@ -1049,7 +553,8 @@ function buildRegisteredTools(api, plugin) {
1049
553
  {
1050
554
  accountId: 'claworld',
1051
555
  worldId: 'dating-demo-world',
1052
- targetAgentId: 'agt_runtime_candidate',
556
+ displayName: 'Runtime Candidate',
557
+ agentCode: 'ZX82QP',
1053
558
  openingMessage: 'Hi, want to compare trail-running routes in Shanghai?',
1054
559
  },
1055
560
  ],
@@ -1058,7 +563,8 @@ function buildRegisteredTools(api, plugin) {
1058
563
  const context = await resolveToolContext(api, plugin, params);
1059
564
  const payload = await plugin.helpers.social.requestChat({
1060
565
  ...context,
1061
- targetAgentId: params.targetAgentId,
566
+ displayName: params.displayName,
567
+ agentCode: params.agentCode,
1062
568
  openingMessage: params.openingMessage || null,
1063
569
  worldId: params.worldId || null,
1064
570
  });
@@ -1332,6 +838,7 @@ function buildRegisteredTools(api, plugin) {
1332
838
  usageNotes: [
1333
839
  'Default action is view. It runs the readiness/binding check and returns the current public identity state.',
1334
840
  'Use action=update_identity after the user confirms a public-facing display name.',
841
+ 'Use action=update_profile to store one global plain-text profile for the current account.',
1335
842
  'Set generateShareCard=true to return a temporary public identity card URL.',
1336
843
  ],
1337
844
  examples: [
@@ -1353,6 +860,15 @@ function buildRegisteredTools(api, plugin) {
1353
860
  },
1354
861
  outcome: 'Persists the display name, keeps the stable code, and returns a temporary share-card URL.',
1355
862
  },
863
+ {
864
+ title: 'Update the global profile',
865
+ input: {
866
+ accountId: 'claworld',
867
+ action: 'update_profile',
868
+ profile: '喜欢慢节奏介绍和小范围世界,也愿意先让 agent 帮我做初步认识。🙂',
869
+ },
870
+ outcome: 'Stores the current account profile text. Pass an empty string to clear it.',
871
+ },
1356
872
  {
1357
873
  title: 'Update the inbound chat policy',
1358
874
  input: {
@@ -1375,15 +891,19 @@ function buildRegisteredTools(api, plugin) {
1375
891
  properties: {
1376
892
  accountId: accountIdProperty,
1377
893
  action: stringParam({
1378
- description: 'Account action. Defaults to view; inferred from displayName or chatRequestApprovalPolicy when omitted.',
894
+ description: 'Account action. Defaults to view; inferred from displayName, profile, or chatRequestApprovalPolicy when omitted.',
1379
895
  enumValues: ACCOUNT_ACTIONS,
1380
- examples: ['view', 'update_identity', 'update_chat_policy'],
896
+ examples: ['view', 'update_identity', 'update_profile', 'update_chat_policy'],
1381
897
  }),
1382
898
  displayName: stringParam({
1383
899
  description: 'Public-facing display name. Required for action=update_identity. # is reserved and must not appear here.',
1384
900
  minLength: 1,
1385
901
  examples: ['Moza', '小发发'],
1386
902
  }),
903
+ profile: stringParam({
904
+ description: 'Global plain-text profile for this account. Maximum 200 characters. Use an empty string to clear it. HTML is not supported.',
905
+ examples: ['喜欢慢节奏介绍和小范围世界,也愿意先让 agent 帮我做初步认识。🙂'],
906
+ }),
1387
907
  chatRequestApprovalPolicy: chatRequestApprovalPolicyProperty,
1388
908
  generateShareCard: booleanParam({
1389
909
  description: 'When true, return a temporary public identity card URL. Defaults to false for view and true for update_identity.',
@@ -1405,6 +925,11 @@ function buildRegisteredTools(api, plugin) {
1405
925
  displayName: '小发发',
1406
926
  generateShareCard: true,
1407
927
  },
928
+ {
929
+ accountId: 'claworld',
930
+ action: 'update_profile',
931
+ profile: '喜欢慢节奏介绍和小范围世界,也愿意先让 agent 帮我做初步认识。🙂',
932
+ },
1408
933
  {
1409
934
  accountId: 'claworld',
1410
935
  action: 'update_chat_policy',
@@ -1440,6 +965,22 @@ function buildRegisteredTools(api, plugin) {
1440
965
  }));
1441
966
  }
1442
967
 
968
+ if (action === 'update_profile') {
969
+ const context = await resolveToolContext(api, plugin, params);
970
+ if (!Object.prototype.hasOwnProperty.call(params, 'profile')) {
971
+ requireManageWorldField('profile', 'profile is required for action=update_profile');
972
+ }
973
+ const payload = await plugin.runtime.productShell.profile.updateProfile({
974
+ ...context,
975
+ profile: params.profile == null ? '' : String(params.profile),
976
+ });
977
+ return buildToolResult(projectToolAccountMutationResponse({
978
+ action,
979
+ accountId: context.accountId,
980
+ identityPayload: payload,
981
+ }));
982
+ }
983
+
1443
984
  if (action === 'update_chat_policy') {
1444
985
  const context = await resolveToolContext(api, plugin, params);
1445
986
  const chatRequestApprovalPolicy = normalizeObject(params.chatRequestApprovalPolicy, null);
@@ -1463,20 +1004,47 @@ function buildRegisteredTools(api, plugin) {
1463
1004
  const cfg = await loadCurrentConfig(api);
1464
1005
  const accountId = normalizeText(params.accountId, plugin.config.defaultAccountId(cfg) || null);
1465
1006
  const runtimeConfig = plugin.config.resolveRuntimeConfig(cfg, accountId);
1466
- const pairingPayload = await plugin.helpers.pairing.ensureAgentPairing({
1467
- cfg,
1468
- accountId,
1469
- runtimeConfig,
1470
- });
1471
- const pairedAgentId = pairingPayload.runtimeConfig?.relay?.agentId || pairingPayload.relayAgent?.agentId || null;
1472
1007
  const identityPayload = await plugin.runtime.productShell.profile.getPublicIdentity({
1473
1008
  cfg,
1474
1009
  accountId,
1475
- runtimeConfig: pairingPayload.runtimeConfig || runtimeConfig,
1476
- agentId: pairedAgentId,
1010
+ runtimeConfig,
1011
+ agentId: runtimeConfig.relay?.agentId || null,
1477
1012
  generateShareCard,
1478
1013
  expiresInSeconds: params.expiresInSeconds ?? null,
1479
1014
  });
1015
+ const pairedAgentId = identityPayload?.agentId || runtimeConfig.relay?.agentId || null;
1016
+ const relayAgent = pairedAgentId
1017
+ ? await plugin.helpers.pairing.resolveAgentIdentity({
1018
+ cfg,
1019
+ accountId,
1020
+ runtimeConfig,
1021
+ agentId: pairedAgentId,
1022
+ })
1023
+ : null;
1024
+ const hasConfiguredAppToken = Boolean(
1025
+ runtimeConfig.appToken
1026
+ || runtimeConfig.relay?.appToken
1027
+ || runtimeConfig.relay?.credentialToken,
1028
+ );
1029
+ const pairingPayload = {
1030
+ status: hasConfiguredAppToken ? 'paired' : 'unpaired',
1031
+ reason: hasConfiguredAppToken
1032
+ ? (pairedAgentId ? null : 'missing_agent_id')
1033
+ : 'missing_app_token',
1034
+ bindingSource: hasConfiguredAppToken
1035
+ ? 'configured_app_token'
1036
+ : (runtimeConfig.registration?.enabled === true ? 'registration_pending' : 'unbound'),
1037
+ runtimeConfig: pairedAgentId
1038
+ ? {
1039
+ ...runtimeConfig,
1040
+ relay: {
1041
+ ...(runtimeConfig.relay && typeof runtimeConfig.relay === 'object' ? runtimeConfig.relay : {}),
1042
+ agentId: pairedAgentId,
1043
+ },
1044
+ }
1045
+ : runtimeConfig,
1046
+ relayAgent,
1047
+ };
1480
1048
  return buildToolResult(projectToolAccountViewResponse({
1481
1049
  accountId,
1482
1050
  pairingPayload,