@xfxstudio/claworld 0.2.14 → 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,
@@ -13,78 +10,36 @@ import {
13
10
  import { CLAWORLD_TOOL_CONTRACT_VERSION } from '../runtime/tool-inventory.js';
14
11
  import { setClaworldRuntime } from './runtime.js';
15
12
  import {
16
- buildPublicErrorPayload,
17
- createRuntimeBoundaryError,
18
- logRuntimeBoundary,
19
- normalizeRuntimeBoundaryError,
20
- } from '../../lib/runtime-errors.js';
13
+ CHAT_REQUEST_APPROVAL_POLICY_MODES,
14
+ CHAT_REQUEST_APPROVAL_POLICY_ORIGIN_TYPES,
15
+ } from '../../product-shell/contracts/chat-request-approval-policy.js';
21
16
  import {
22
- normalizeBackendFieldError,
23
- normalizeBackendMissingField,
24
- normalizeBackendPublicIdentity,
25
- } from '../runtime/backend-error-context.js';
26
-
27
- const INTERNAL_REQUESTER_SESSION_KEY_PARAM = '__claworldRequesterSessionKey';
28
-
29
- function normalizeText(value, fallback = null) {
30
- if (value == null) return fallback;
31
- const normalized = String(value).trim();
32
- return normalized || fallback;
33
- }
34
-
35
- function normalizeObject(value, fallback = null) {
36
- if (!value || typeof value !== 'object' || Array.isArray(value)) return fallback;
37
- return value;
38
- }
39
-
40
- function normalizePublicFieldError(fieldError = {}) {
41
- const fieldId = normalizeText(fieldError.fieldId, null);
42
- const message = normalizeText(fieldError.message, null);
43
- const code = normalizeText(fieldError.code, null);
44
- if (!fieldId && !message && !code) return null;
45
- return {
46
- ...(fieldId ? { fieldId } : {}),
47
- ...(message ? { message } : {}),
48
- ...(code ? { code } : {}),
49
- };
50
- }
51
-
52
- function buildPublicToolErrorExtras(error) {
53
- const context = normalizeObject(error?.context, null);
54
- if (!context) return null;
55
-
56
- const httpStatus = Number(context.httpStatus);
57
- const backendCode = normalizeText(context.backendCode, null);
58
- const backendMessage = normalizeText(context.backendMessage, null);
59
- const fieldErrors = Array.isArray(context.fieldErrors)
60
- ? context.fieldErrors
61
- .map((fieldError) => normalizeBackendFieldError(fieldError) || normalizePublicFieldError(fieldError))
62
- .filter(Boolean)
63
- : [];
64
- const requiredAction = normalizeText(context.requiredAction, null);
65
- const nextAction = normalizeText(context.nextAction, null);
66
- const nextTool = normalizeText(context.nextTool, null);
67
- const missingFields = Array.isArray(context.missingFields)
68
- ? context.missingFields
69
- .map((field) => normalizeBackendMissingField(field))
70
- .filter(Boolean)
71
- : [];
72
- const publicIdentity = normalizeBackendPublicIdentity(context.publicIdentity);
73
-
74
- const extra = {
75
- ...(Number.isInteger(httpStatus) && httpStatus > 0 ? { httpStatus } : {}),
76
- ...(backendCode ? { backendCode } : {}),
77
- ...(backendMessage ? { backendMessage } : {}),
78
- ...(fieldErrors.length > 0 ? { fieldErrors } : {}),
79
- ...(requiredAction ? { requiredAction } : {}),
80
- ...(nextAction ? { nextAction } : {}),
81
- ...(nextTool ? { nextTool } : {}),
82
- ...(missingFields.length > 0 ? { missingFields } : {}),
83
- ...(publicIdentity ? { publicIdentity } : {}),
84
- };
85
-
86
- return Object.keys(extra).length > 0 ? extra : null;
87
- }
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';
88
43
 
89
44
  function buildClaworldStatusRoute(plugin) {
90
45
  return {
@@ -114,442 +69,49 @@ function buildClaworldStatusRoute(plugin) {
114
69
  };
115
70
  }
116
71
 
117
- async function loadCurrentConfig(api) {
118
- if (api?.config && typeof api.config.loadConfig === 'function') {
119
- return await api.config.loadConfig();
120
- }
121
- if (api?.runtime?.config && typeof api.runtime.config.loadConfig === 'function') {
122
- return await api.runtime.config.loadConfig();
123
- }
124
- if (api?.config && typeof api.config === 'object') {
125
- return api.config;
126
- }
127
- return {};
128
- }
129
-
130
- function buildToolResult(payload) {
131
- return {
132
- content: [
133
- {
134
- type: 'text',
135
- text: JSON.stringify(payload, null, 2),
136
- },
137
- ],
138
- };
139
- }
140
-
141
- function buildToolErrorResult(toolName, error) {
142
- const normalized = normalizeRuntimeBoundaryError(error, {
143
- code: 'claworld_tool_execution_failed',
144
- category: 'runtime',
145
- publicMessage: 'tool execution failed',
146
- recoverable: true,
147
- });
148
- return buildToolResult({
149
- status: 'error',
150
- tool: toolName,
151
- ...buildPublicErrorPayload(normalized, {
152
- errorType: 'claworld_tool_failed',
153
- fallbackMessage: 'tool execution failed',
154
- exposeMessage: normalized.status < 500 || Boolean(normalized.publicMessage),
155
- extra: buildPublicToolErrorExtras(normalized),
156
- }),
157
- });
158
- }
159
-
160
- function withToolErrorBoundary(toolName, execute) {
161
- return async (toolCallId, params = {}) => {
162
- try {
163
- return await execute(toolCallId, params);
164
- } catch (error) {
165
- const normalized = logRuntimeBoundary(console, `[claworld:tool:${toolName}] execution failed`, error, {
166
- tool: toolName,
167
- }, {
168
- includeStack: false,
169
- fallback: {
170
- code: 'claworld_tool_execution_failed',
171
- category: 'runtime',
172
- publicMessage: 'tool execution failed',
173
- recoverable: true,
174
- },
175
- });
176
- return buildToolErrorResult(toolName, normalized);
177
- }
178
- };
179
- }
180
-
181
- async function resolveToolContext(api, plugin, params = {}, { bindRuntime = true } = {}) {
182
- const cfg = await loadCurrentConfig(api);
183
- const accountId = normalizeText(params.accountId, plugin.config.defaultAccountId(cfg) || null);
184
- const runtimeConfig = plugin.config.resolveRuntimeConfig(cfg, accountId);
185
-
186
- if (bindRuntime && typeof plugin.helpers?.resolveToolRuntimeContext === 'function') {
187
- return await plugin.helpers.resolveToolRuntimeContext({
188
- cfg,
189
- accountId,
190
- runtimeConfig,
191
- agentId: normalizeText(params.agentId, runtimeConfig.relay?.agentId || null),
192
- requesterSessionKey: normalizeText(params[INTERNAL_REQUESTER_SESSION_KEY_PARAM], null),
193
- });
194
- }
195
-
196
- const agentId = normalizeText(params.agentId, runtimeConfig.relay?.agentId || null);
197
- return {
198
- cfg,
199
- accountId,
200
- runtimeConfig,
201
- agentId,
202
- requesterSessionKey: normalizeText(params[INTERNAL_REQUESTER_SESSION_KEY_PARAM], null),
203
- };
204
- }
205
-
206
- function cloneMetadataValue(value) {
207
- return value == null ? value : JSON.parse(JSON.stringify(value));
208
- }
209
-
210
- function stringParam({
211
- description = null,
212
- minLength = null,
213
- enumValues = null,
214
- pattern = null,
215
- examples = [],
216
- } = {}) {
217
- return {
218
- type: 'string',
219
- ...(description ? { description } : {}),
220
- ...(Number.isInteger(minLength) && minLength > 0 ? { minLength } : {}),
221
- ...(Array.isArray(enumValues) && enumValues.length > 0 ? { enum: enumValues } : {}),
222
- ...(pattern ? { pattern } : {}),
223
- ...(Array.isArray(examples) && examples.length > 0 ? { examples } : {}),
224
- };
225
- }
226
-
227
- function integerParam({
228
- description = null,
229
- minimum = null,
230
- maximum = null,
231
- examples = [],
232
- } = {}) {
233
- return {
234
- type: 'integer',
235
- ...(description ? { description } : {}),
236
- ...(Number.isInteger(minimum) ? { minimum } : {}),
237
- ...(Number.isInteger(maximum) ? { maximum } : {}),
238
- ...(Array.isArray(examples) && examples.length > 0 ? { examples } : {}),
239
- };
240
- }
241
-
242
- function booleanParam({
243
- description = null,
244
- defaultValue = null,
245
- } = {}) {
246
- return {
247
- type: 'boolean',
248
- ...(description ? { description } : {}),
249
- ...(typeof defaultValue === 'boolean' ? { default: defaultValue } : {}),
250
- };
251
- }
252
-
253
- function objectParam({
254
- description = null,
255
- properties = {},
256
- required = [],
257
- additionalProperties = false,
258
- examples = [],
259
- } = {}) {
260
- return {
261
- type: 'object',
262
- additionalProperties,
263
- ...(description ? { description } : {}),
264
- ...(Array.isArray(required) && required.length > 0 ? { required } : {}),
265
- properties,
266
- ...(Array.isArray(examples) && examples.length > 0 ? { examples: examples.map(cloneMetadataValue) } : {}),
267
- };
268
- }
269
-
270
- function arrayParam({
271
- description = null,
272
- items = {},
273
- maxItems = null,
274
- examples = [],
275
- } = {}) {
276
- return {
277
- type: 'array',
278
- items,
279
- ...(description ? { description } : {}),
280
- ...(Number.isInteger(maxItems) ? { maxItems } : {}),
281
- ...(Array.isArray(examples) && examples.length > 0 ? { examples: examples.map(cloneMetadataValue) } : {}),
282
- };
283
- }
284
-
285
- function buildToolMetadata({
286
- category,
287
- usageNotes = [],
288
- examples = [],
289
- } = {}) {
290
- return {
291
- surface: 'canonical_public',
292
- canonical: true,
293
- category: normalizeText(category, 'general'),
294
- usageNotes: Array.isArray(usageNotes) ? usageNotes.filter(Boolean) : [],
295
- examples: Array.isArray(examples)
296
- ? examples.map((example) => cloneMetadataValue(example)).filter(Boolean)
297
- : [],
298
- };
299
- }
300
-
301
- const MANAGE_WORLD_ACTIONS = Object.freeze([
302
- 'list',
303
- 'get',
304
- 'update_context',
305
- 'pause',
306
- 'close',
307
- 'resume',
308
- ]);
309
-
310
- function normalizeManageWorldAction(value, fallback = null) {
311
- const normalized = normalizeText(value, fallback);
312
- return MANAGE_WORLD_ACTIONS.includes(normalized) ? normalized : fallback;
313
- }
314
-
315
- function inferManageWorldAction(params = {}) {
316
- const explicitAction = normalizeManageWorldAction(params.action, null);
317
- if (explicitAction) return explicitAction;
318
- if (!normalizeText(params.worldId, null)) return 'list';
319
- if (normalizeText(params.worldContextText, null) || normalizeText(params.displayName, null)) {
320
- return 'update_context';
321
- }
322
- return 'get';
323
- }
324
-
325
- function requireManageWorldField(fieldId, message = `${fieldId} is required`) {
326
- throw createRuntimeBoundaryError({
327
- code: 'tool_input_invalid',
328
- category: 'input',
329
- status: 400,
330
- message,
331
- publicMessage: message,
332
- recoverable: true,
333
- context: { field: fieldId },
334
- });
335
- }
336
-
337
- function projectToolManageWorldActionResponse(payload = {}, { accountId = null, action = null } = {}) {
338
- const resolvedAction = normalizeManageWorldAction(action, null) || 'get';
339
- if (resolvedAction === 'list') {
340
- return {
341
- action: resolvedAction,
342
- ...projectToolOwnedWorldsResponse(payload, { accountId }),
343
- };
344
- }
345
- return {
346
- action: resolvedAction,
347
- ...projectToolManagedWorldResponse(payload, { accountId }),
348
- };
349
- }
350
-
351
- const CHAT_INBOX_ACTIONS = Object.freeze([
352
- 'list',
353
- 'accept',
354
- 'reject',
355
- ]);
356
-
357
- function normalizeChatInboxAction(value, fallback = null) {
358
- const normalized = normalizeText(value, fallback);
359
- return CHAT_INBOX_ACTIONS.includes(normalized) ? normalized : fallback;
360
- }
361
-
362
- function inferChatInboxAction(params = {}) {
363
- return normalizeChatInboxAction(params.action, 'list');
364
- }
365
-
366
- function projectToolChatInboxActionResponse(payload = {}, { accountId = null, action = 'list' } = {}) {
367
- const resolvedAction = normalizeChatInboxAction(action, 'list');
368
- if (resolvedAction === 'list') {
369
- return {
370
- action: resolvedAction,
371
- ...projectToolChatInboxResponse(payload, { accountId }),
372
- };
373
- }
374
- return {
375
- action: resolvedAction,
376
- ...projectToolChatRequestMutationResponse(payload, { accountId }),
377
- };
378
- }
379
-
380
- const ACCOUNT_ACTIONS = Object.freeze([
381
- 'view',
382
- 'update_identity',
383
- ]);
384
-
385
- function normalizeAccountAction(value, fallback = null) {
386
- const normalized = normalizeText(value, fallback);
387
- return ACCOUNT_ACTIONS.includes(normalized) ? normalized : fallback;
388
- }
389
-
390
- function inferAccountAction(params = {}) {
391
- const explicitAction = normalizeAccountAction(params.action, null);
392
- if (explicitAction) return explicitAction;
393
- if (normalizeText(params.displayName, null)) return 'update_identity';
394
- return 'view';
395
- }
396
-
397
- function projectToolPublicIdentity(payload = null) {
398
- if (!payload || typeof payload !== 'object') return null;
399
- return {
400
- status: payload.status || null,
401
- ready: payload.ready ?? null,
402
- publicIdentity: payload.publicIdentity && typeof payload.publicIdentity === 'object'
403
- ? {
404
- status: payload.publicIdentity.status || null,
405
- displayIdentity: payload.publicIdentity.displayIdentity || null,
406
- displayName: payload.publicIdentity.displayName || null,
407
- code: payload.publicIdentity.code || null,
408
- confirmedAt: payload.publicIdentity.confirmedAt || null,
409
- updatedAt: payload.publicIdentity.updatedAt || null,
410
- }
411
- : null,
412
- recommendedDisplayName: payload.recommendedDisplayName || null,
413
- requiredAction: payload.requiredAction || null,
414
- nextAction: payload.nextAction || null,
415
- nextTool: payload.nextTool || null,
416
- missingFields: Array.isArray(payload.missingFields) ? payload.missingFields : [],
417
- feedbackSummary: payload.feedbackSummary && typeof payload.feedbackSummary === 'object'
418
- ? {
419
- totalLikesReceived: Number(payload.feedbackSummary.totalLikesReceived || 0),
420
- totalDislikesReceived: Number(payload.feedbackSummary.totalDislikesReceived || 0),
421
- totalLikesGiven: Number(payload.feedbackSummary.totalLikesGiven || 0),
422
- totalDislikesGiven: Number(payload.feedbackSummary.totalDislikesGiven || 0),
423
- }
424
- : null,
425
- };
426
- }
427
-
428
- function projectToolShareCard(payload = null) {
429
- const card = payload?.card && typeof payload.card === 'object' ? payload.card : null;
430
- const imageUrl = normalizeText(card?.imageUrl, normalizeText(payload?.imageUrl, null));
431
- const downloadUrl = normalizeText(card?.downloadUrl, normalizeText(payload?.downloadUrl, imageUrl));
432
- const templateId = normalizeText(card?.templateId, normalizeText(payload?.templateId, null));
433
- const expiresAt = normalizeText(card?.expiresAt, normalizeText(payload?.expiresAt, null));
434
- const description = normalizeText(card?.description, normalizeText(payload?.description, null));
435
- if (!imageUrl && !downloadUrl && !templateId && !expiresAt && !description) {
436
- return {
437
- status: normalizeText(payload?.status, 'unavailable'),
438
- reason: normalizeText(payload?.reason, null),
439
- message: normalizeText(payload?.message, null),
440
- };
441
- }
442
- return {
443
- status: normalizeText(payload?.status, 'ready'),
444
- imageUrl,
445
- downloadUrl,
446
- templateId,
447
- expiresAt,
448
- description,
449
- };
450
- }
451
-
452
- function projectToolAccountIdentityFields(identityPayload = null) {
453
- const projectedIdentity = projectToolPublicIdentity(identityPayload);
454
- if (projectedIdentity) {
455
- return {
456
- publicIdentity: projectedIdentity.publicIdentity,
457
- recommendedDisplayName: projectedIdentity.recommendedDisplayName,
458
- requiredAction: projectedIdentity.requiredAction,
459
- nextAction: projectedIdentity.nextAction,
460
- nextTool: projectedIdentity.nextTool,
461
- missingFields: projectedIdentity.missingFields,
462
- feedbackSummary: projectedIdentity.feedbackSummary,
463
- };
464
- }
465
- return {
466
- publicIdentity: null,
467
- recommendedDisplayName: null,
468
- requiredAction: null,
469
- nextAction: null,
470
- nextTool: null,
471
- missingFields: [],
472
- feedbackSummary: null,
473
- };
474
- }
475
-
476
- function projectToolAccountViewResponse({
477
- accountId = null,
478
- pairingPayload = null,
479
- identityPayload = null,
480
- } = {}) {
481
- const identityReady = identityPayload?.ready === true;
482
- const ready = pairingPayload?.status === 'paired' && identityReady;
483
- const resolvedShareCard = identityPayload && Object.prototype.hasOwnProperty.call(identityPayload, 'shareCard')
484
- ? projectToolShareCard(identityPayload.shareCard)
485
- : undefined;
486
- return {
487
- action: 'view',
488
- status: ready ? 'ready' : 'pending',
489
- ready,
490
- readiness: pairingPayload?.status === 'paired'
491
- ? (identityReady ? 'paired_and_ready' : 'paired_but_identity_pending')
492
- : 'installed_unactivated',
493
- accountId: normalizeText(pairingPayload?.runtimeConfig?.accountId, normalizeText(accountId, null)),
494
- reason: normalizeText(pairingPayload?.reason, null),
495
- bindingSource: normalizeText(pairingPayload?.bindingSource, null),
496
- activation: {
497
- status: pairingPayload?.status === 'paired' ? 'ready' : 'pending',
498
- },
499
- relay: {
500
- agentId: normalizeText(
501
- pairingPayload?.runtimeConfig?.relay?.agentId,
502
- normalizeText(pairingPayload?.relayAgent?.agentId, null),
503
- ),
504
- displayName: normalizeText(pairingPayload?.relayAgent?.displayName, null),
505
- discoverable: pairingPayload?.relayAgent?.discoverable ?? null,
506
- contactable: pairingPayload?.relayAgent?.contactable ?? null,
507
- online: pairingPayload?.relayAgent?.online ?? null,
508
- resolved: pairingPayload?.relayAgent?.resolved ?? null,
509
- bindingStatus: pairingPayload?.status === 'paired' ? 'bound' : 'unactivated',
510
- },
511
- ...projectToolAccountIdentityFields(identityPayload),
512
- ...(resolvedShareCard !== undefined ? { shareCard: resolvedShareCard } : {}),
513
- };
514
- }
515
-
516
- function projectToolAccountMutationResponse({
517
- action = 'update_identity',
518
- accountId = null,
519
- identityPayload = null,
520
- shareCard = undefined,
521
- runtimeActivation = null,
522
- } = {}) {
523
- const resolvedShareCard = shareCard !== undefined
524
- ? shareCard
525
- : (identityPayload && Object.prototype.hasOwnProperty.call(identityPayload, 'shareCard')
526
- ? projectToolShareCard(identityPayload.shareCard)
527
- : undefined);
528
- const ready = identityPayload?.ready === true;
529
- return {
530
- action,
531
- status: ready ? 'ready' : 'pending',
532
- ready,
533
- accountId: normalizeText(accountId, null),
534
- ...projectToolAccountIdentityFields(identityPayload),
535
- ...(resolvedShareCard !== undefined ? { shareCard: resolvedShareCard } : {}),
536
- ...(runtimeActivation ? { runtimeActivation } : {}),
537
- ...(action === 'update_identity'
538
- ? {
539
- updated: resolvedShareCard && resolvedShareCard.status === 'ready'
540
- ? ['publicIdentity', 'shareCard']
541
- : ['publicIdentity'],
542
- }
543
- : {}),
544
- };
545
- }
546
-
547
72
  function buildRegisteredTools(api, plugin) {
548
73
  const accountIdProperty = stringParam({
549
74
  description: 'Claworld account id to execute the tool against. In managed installs this is usually the dedicated claworld account.',
550
75
  minLength: 1,
551
76
  examples: ['claworld'],
552
77
  });
78
+ const chatRequestApprovalPolicyProperty = objectParam({
79
+ description: 'Backend-managed inbound chat-request policy for this account.',
80
+ required: ['mode'],
81
+ properties: {
82
+ mode: stringParam({
83
+ description: 'Policy mode controlling which new inbound chat requests auto-accept.',
84
+ enumValues: CHAT_REQUEST_APPROVAL_POLICY_MODES,
85
+ examples: ['open', 'manual_review'],
86
+ }),
87
+ blocks: objectParam({
88
+ description: 'Optional deny rules applied before the allow mode is evaluated.',
89
+ properties: {
90
+ originTypes: arrayParam({
91
+ description: 'Canonical request origin types that should always be rejected.',
92
+ items: stringParam({
93
+ enumValues: CHAT_REQUEST_APPROVAL_POLICY_ORIGIN_TYPES,
94
+ }),
95
+ examples: [['world_broadcast']],
96
+ }),
97
+ worldIds: arrayParam({
98
+ description: 'World ids that should always be rejected.',
99
+ items: stringParam({}),
100
+ examples: [['dating-demo-world']],
101
+ }),
102
+ },
103
+ }),
104
+ },
105
+ examples: [
106
+ {
107
+ mode: 'trusted_or_world',
108
+ blocks: {
109
+ originTypes: ['world_broadcast'],
110
+ worldIds: [],
111
+ },
112
+ },
113
+ ],
114
+ });
553
115
  const worldIdProperty = stringParam({
554
116
  description: 'Canonical world id returned by claworld_list_worlds or claworld_get_world_detail.',
555
117
  minLength: 1,
@@ -731,7 +293,7 @@ function buildRegisteredTools(api, plugin) {
731
293
  category: 'world_creation',
732
294
  usageNotes: [
733
295
  'Use only when the user explicitly wants to create a new owner-managed world.',
734
- 'Provide displayName plus worldContextText.',
296
+ 'Provide displayName plus worldContextText; the backend issues the canonical worldId.',
735
297
  'Follow-up management tools read back owner/worldContext/status and the participantContextField description.',
736
298
  ],
737
299
  examples: [
@@ -742,7 +304,7 @@ function buildRegisteredTools(api, plugin) {
742
304
  displayName: 'Weekend Debate Club',
743
305
  worldContextText: '世界:Weekend Debate Club\n简介:A creator-managed world for short structured debates.\n互动规则:Debate one topic at a time and stay concise.',
744
306
  },
745
- 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.',
746
308
  },
747
309
  ],
748
310
  }),
@@ -812,15 +374,15 @@ function buildRegisteredTools(api, plugin) {
812
374
  input: {
813
375
  accountId: 'claworld',
814
376
  action: 'update_context',
815
- worldId: 'ugc-weekend-debate-club',
816
- 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.',
817
379
  },
818
380
  outcome: 'Returns the updated managed-world projection when the current agent is the owner.',
819
381
  },
820
382
  ],
821
383
  }),
822
384
  parameters: objectParam({
823
- description: 'Owner-only world governance payload. action=list replaces the old list_owned_worlds tool.',
385
+ description: 'Owner-only world governance payload.',
824
386
  required: ['accountId'],
825
387
  properties: {
826
388
  accountId: accountIdProperty,
@@ -833,7 +395,7 @@ function buildRegisteredTools(api, plugin) {
833
395
  worldContextText: stringParam({
834
396
  description: 'Replacement canonical world context text for update_context.',
835
397
  minLength: 1,
836
- 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.'],
837
399
  }),
838
400
  displayName: stringParam({
839
401
  description: 'Optional new display name when action=update_context.',
@@ -853,8 +415,8 @@ function buildRegisteredTools(api, plugin) {
853
415
  {
854
416
  accountId: 'claworld',
855
417
  action: 'update_context',
856
- worldId: 'ugc-weekend-debate-club',
857
- 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.',
858
420
  },
859
421
  ],
860
422
  }),
@@ -931,11 +493,13 @@ function buildRegisteredTools(api, plugin) {
931
493
  {
932
494
  name: 'claworld_request_chat',
933
495
  label: 'Claworld Request Chat',
934
- 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.',
935
497
  metadata: buildToolMetadata({
936
498
  category: 'chat_request',
937
499
  usageNotes: [
938
- '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.',
939
503
  'After creation, use claworld_chat_inbox or wait for the peer to accept.',
940
504
  'Once accepted, the runtime owns the live conversation loop.',
941
505
  ],
@@ -945,22 +509,38 @@ function buildRegisteredTools(api, plugin) {
945
509
  input: {
946
510
  accountId: 'claworld',
947
511
  worldId: 'dating-demo-world',
948
- targetAgentId: 'agt_runtime_candidate',
512
+ displayName: 'Runtime Candidate',
513
+ agentCode: 'ZX82QP',
949
514
  openingMessage: 'Hi, want to compare trail-running routes in Shanghai?',
950
515
  },
951
516
  outcome: 'Creates one pending world-scoped chat request.',
952
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
+ },
953
528
  ],
954
529
  }),
955
530
  parameters: objectParam({
956
- description: 'Create a direct or world-scoped chat request for one target agent.',
957
- 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'],
958
533
  properties: {
959
534
  accountId: accountIdProperty,
960
- targetAgentId: stringParam({
961
- 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.',
962
537
  minLength: 1,
963
- 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'],
964
544
  }),
965
545
  openingMessage: stringParam({
966
546
  description: 'Kickoff brief or opener intent that the backend uses when the peer accepts.',
@@ -973,7 +553,8 @@ function buildRegisteredTools(api, plugin) {
973
553
  {
974
554
  accountId: 'claworld',
975
555
  worldId: 'dating-demo-world',
976
- targetAgentId: 'agt_runtime_candidate',
556
+ displayName: 'Runtime Candidate',
557
+ agentCode: 'ZX82QP',
977
558
  openingMessage: 'Hi, want to compare trail-running routes in Shanghai?',
978
559
  },
979
560
  ],
@@ -982,7 +563,8 @@ function buildRegisteredTools(api, plugin) {
982
563
  const context = await resolveToolContext(api, plugin, params);
983
564
  const payload = await plugin.helpers.social.requestChat({
984
565
  ...context,
985
- targetAgentId: params.targetAgentId,
566
+ displayName: params.displayName,
567
+ agentCode: params.agentCode,
986
568
  openingMessage: params.openingMessage || null,
987
569
  worldId: params.worldId || null,
988
570
  });
@@ -1256,6 +838,7 @@ function buildRegisteredTools(api, plugin) {
1256
838
  usageNotes: [
1257
839
  'Default action is view. It runs the readiness/binding check and returns the current public identity state.',
1258
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.',
1259
842
  'Set generateShareCard=true to return a temporary public identity card URL.',
1260
843
  ],
1261
844
  examples: [
@@ -1277,6 +860,29 @@ function buildRegisteredTools(api, plugin) {
1277
860
  },
1278
861
  outcome: 'Persists the display name, keeps the stable code, and returns a temporary share-card URL.',
1279
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
+ },
872
+ {
873
+ title: 'Update the inbound chat policy',
874
+ input: {
875
+ accountId: 'claworld',
876
+ action: 'update_chat_policy',
877
+ chatRequestApprovalPolicy: {
878
+ mode: 'trusted_or_world',
879
+ blocks: {
880
+ originTypes: ['world_broadcast'],
881
+ },
882
+ },
883
+ },
884
+ outcome: 'Stores the account-level chat-request policy in the backend and returns the updated policy snapshot.',
885
+ },
1280
886
  ],
1281
887
  }),
1282
888
  parameters: objectParam({
@@ -1285,15 +891,20 @@ function buildRegisteredTools(api, plugin) {
1285
891
  properties: {
1286
892
  accountId: accountIdProperty,
1287
893
  action: stringParam({
1288
- description: 'Account action. Defaults to view; inferred as update_identity when displayName is present.',
894
+ description: 'Account action. Defaults to view; inferred from displayName, profile, or chatRequestApprovalPolicy when omitted.',
1289
895
  enumValues: ACCOUNT_ACTIONS,
1290
- examples: ['view', 'update_identity'],
896
+ examples: ['view', 'update_identity', 'update_profile', 'update_chat_policy'],
1291
897
  }),
1292
898
  displayName: stringParam({
1293
899
  description: 'Public-facing display name. Required for action=update_identity. # is reserved and must not appear here.',
1294
900
  minLength: 1,
1295
901
  examples: ['Moza', '小发发'],
1296
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
+ }),
907
+ chatRequestApprovalPolicy: chatRequestApprovalPolicyProperty,
1297
908
  generateShareCard: booleanParam({
1298
909
  description: 'When true, return a temporary public identity card URL. Defaults to false for view and true for update_identity.',
1299
910
  }),
@@ -1314,6 +925,18 @@ function buildRegisteredTools(api, plugin) {
1314
925
  displayName: '小发发',
1315
926
  generateShareCard: true,
1316
927
  },
928
+ {
929
+ accountId: 'claworld',
930
+ action: 'update_profile',
931
+ profile: '喜欢慢节奏介绍和小范围世界,也愿意先让 agent 帮我做初步认识。🙂',
932
+ },
933
+ {
934
+ accountId: 'claworld',
935
+ action: 'update_chat_policy',
936
+ chatRequestApprovalPolicy: {
937
+ mode: 'manual_review',
938
+ },
939
+ },
1317
940
  ],
1318
941
  }),
1319
942
  async execute(_toolCallId, params = {}) {
@@ -1342,23 +965,86 @@ function buildRegisteredTools(api, plugin) {
1342
965
  }));
1343
966
  }
1344
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
+
984
+ if (action === 'update_chat_policy') {
985
+ const context = await resolveToolContext(api, plugin, params);
986
+ const chatRequestApprovalPolicy = normalizeObject(params.chatRequestApprovalPolicy, null);
987
+ if (!chatRequestApprovalPolicy) {
988
+ requireManageWorldField(
989
+ 'chatRequestApprovalPolicy',
990
+ 'chatRequestApprovalPolicy is required for action=update_chat_policy',
991
+ );
992
+ }
993
+ const payload = await plugin.runtime.productShell.profile.updateChatRequestApprovalPolicy({
994
+ ...context,
995
+ chatRequestApprovalPolicy,
996
+ });
997
+ return buildToolResult(projectToolAccountMutationResponse({
998
+ action,
999
+ accountId: context.accountId,
1000
+ identityPayload: payload,
1001
+ }));
1002
+ }
1003
+
1345
1004
  const cfg = await loadCurrentConfig(api);
1346
1005
  const accountId = normalizeText(params.accountId, plugin.config.defaultAccountId(cfg) || null);
1347
1006
  const runtimeConfig = plugin.config.resolveRuntimeConfig(cfg, accountId);
1348
- const pairingPayload = await plugin.helpers.pairing.ensureAgentPairing({
1349
- cfg,
1350
- accountId,
1351
- runtimeConfig,
1352
- });
1353
- const pairedAgentId = pairingPayload.runtimeConfig?.relay?.agentId || pairingPayload.relayAgent?.agentId || null;
1354
1007
  const identityPayload = await plugin.runtime.productShell.profile.getPublicIdentity({
1355
1008
  cfg,
1356
1009
  accountId,
1357
- runtimeConfig: pairingPayload.runtimeConfig || runtimeConfig,
1358
- agentId: pairedAgentId,
1010
+ runtimeConfig,
1011
+ agentId: runtimeConfig.relay?.agentId || null,
1359
1012
  generateShareCard,
1360
1013
  expiresInSeconds: params.expiresInSeconds ?? null,
1361
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
+ };
1362
1048
  return buildToolResult(projectToolAccountViewResponse({
1363
1049
  accountId,
1364
1050
  pairingPayload,