@xfxstudio/claworld 2026.4.29-testing.1 → 2026.4.29-testing.3

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.
@@ -8,7 +8,7 @@
8
8
  ],
9
9
  "name": "Claworld Persona Relay",
10
10
  "description": "Claworld relay world channel plugin for OpenClaw.",
11
- "version": "2026.4.29-testing.1",
11
+ "version": "2026.4.29-testing.3",
12
12
  "configSchema": {
13
13
  "type": "object",
14
14
  "additionalProperties": false,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xfxstudio/claworld",
3
- "version": "2026.4.29-testing.1",
3
+ "version": "2026.4.29-testing.3",
4
4
  "description": "Claworld channel plugin for OpenClaw",
5
5
  "type": "module",
6
6
  "main": "index.js",
@@ -142,6 +142,33 @@ const CHAT_INBOX_FILTER_STATUSES = Object.freeze([
142
142
  'kickoff_failed',
143
143
  'ended',
144
144
  ]);
145
+ const CHAT_INBOX_FILTER_KEYS = Object.freeze([
146
+ 'direction',
147
+ 'mode',
148
+ 'status',
149
+ 'worldId',
150
+ 'chatRequestId',
151
+ 'conversationKey',
152
+ 'localSessionKey',
153
+ 'counterpartyAgentId',
154
+ ]);
155
+ const CHAT_INBOX_FILTER_KEY_SET = new Set(CHAT_INBOX_FILTER_KEYS);
156
+ const MANAGE_CONVERSATION_REQUEST_ONLY_QUERY_FIELDS = Object.freeze([
157
+ 'displayName',
158
+ 'agentCode',
159
+ 'openingMessage',
160
+ ]);
161
+ const MANAGE_CONVERSATION_FILTER_ONLY_TOP_LEVEL_FIELDS = Object.freeze([
162
+ 'mode',
163
+ 'status',
164
+ 'worldId',
165
+ 'counterpartyAgentId',
166
+ ]);
167
+ const MANAGE_CONVERSATION_GET_STATE_TARGET_FIELDS = Object.freeze([
168
+ 'chatRequestId',
169
+ 'conversationKey',
170
+ 'localSessionKey',
171
+ ]);
145
172
 
146
173
  const TERMINAL_ACCOUNT_ACTIONS = Object.freeze([
147
174
  'view_account',
@@ -292,6 +319,117 @@ function normalizeChatInboxListFiltersInput(params = {}) {
292
319
  );
293
320
  }
294
321
 
322
+ function hasProvidedToolParam(params = {}, fieldId) {
323
+ if (!params || typeof params !== 'object') return false;
324
+ if (!Object.prototype.hasOwnProperty.call(params, fieldId)) return false;
325
+ const value = params[fieldId];
326
+ if (typeof value === 'string') return normalizeText(value, null) != null;
327
+ return value != null;
328
+ }
329
+
330
+ function buildChatInboxFiltersParam({ description, worldIdProperty } = {}) {
331
+ return objectParam({
332
+ description,
333
+ additionalProperties: false,
334
+ properties: {
335
+ direction: stringParam({
336
+ description: 'Filter from the current account perspective.',
337
+ enumValues: CHAT_INBOX_FILTER_DIRECTIONS,
338
+ examples: ['outbound'],
339
+ }),
340
+ mode: stringParam({
341
+ description: 'Filter to direct or world-scoped chat items.',
342
+ enumValues: CHAT_INBOX_FILTER_MODES,
343
+ examples: ['world'],
344
+ }),
345
+ status: stringParam({
346
+ description: 'Filter to pending or terminal requests, or to chats by current status.',
347
+ enumValues: CHAT_INBOX_FILTER_STATUSES,
348
+ examples: ['active'],
349
+ }),
350
+ worldId: worldIdProperty,
351
+ chatRequestId: stringParam({
352
+ description: 'Filter to one canonical chat request id.',
353
+ minLength: 1,
354
+ examples: ['req_demo_1'],
355
+ }),
356
+ conversationKey: stringParam({
357
+ description: 'Filter to one canonical conversation key.',
358
+ minLength: 1,
359
+ examples: ['pair:agt_alice::agt_moza:world:dating-demo-world'],
360
+ }),
361
+ localSessionKey: stringParam({
362
+ description: 'Filter to one local Claworld session reference for internal tracking, summaries, or orchestration only. Not a transport address for sending a user message to the peer.',
363
+ minLength: 1,
364
+ examples: ['conversation:pair:agt_alice::agt_moza:world:dating-demo-world'],
365
+ }),
366
+ counterpartyAgentId: stringParam({
367
+ description: 'Filter to one counterparty agentId.',
368
+ minLength: 1,
369
+ examples: ['agt_alice'],
370
+ }),
371
+ },
372
+ });
373
+ }
374
+
375
+ function validateChatInboxFilterInput(filters = {}, action) {
376
+ const source = normalizeObject(filters, {}) || {};
377
+ for (const key of Object.keys(source)) {
378
+ if (CHAT_INBOX_FILTER_KEY_SET.has(key)) continue;
379
+ requireManageWorldField(`filters.${key}`, `filters.${key} is not supported for action=${action}`);
380
+ }
381
+ return source;
382
+ }
383
+
384
+ function normalizeManageConversationInboxQuery(params = {}, action) {
385
+ const normalizedAction = normalizeTerminalConversationAction(action, 'list_related');
386
+ const filters = validateChatInboxFilterInput(params.filters, normalizedAction);
387
+
388
+ const requestOnlyField = MANAGE_CONVERSATION_REQUEST_ONLY_QUERY_FIELDS.find((fieldId) => hasProvidedToolParam(params, fieldId));
389
+ if (requestOnlyField) {
390
+ requireManageWorldField(requestOnlyField, `${requestOnlyField} is only supported for action=request`);
391
+ }
392
+ if (hasProvidedToolParam(params, 'limit')) {
393
+ requireManageWorldField('limit', `limit is not supported for action=${normalizedAction}`);
394
+ }
395
+
396
+ const filterOnlyField = MANAGE_CONVERSATION_FILTER_ONLY_TOP_LEVEL_FIELDS.find((fieldId) => hasProvidedToolParam(params, fieldId));
397
+ if (filterOnlyField) {
398
+ requireManageWorldField(
399
+ filterOnlyField,
400
+ `${filterOnlyField} must be passed as filters.${filterOnlyField} for action=${normalizedAction}`,
401
+ );
402
+ }
403
+
404
+ if (normalizedAction !== 'get_state') {
405
+ const getStateOnlyField = MANAGE_CONVERSATION_GET_STATE_TARGET_FIELDS.find((fieldId) => hasProvidedToolParam(params, fieldId));
406
+ if (getStateOnlyField) {
407
+ requireManageWorldField(
408
+ getStateOnlyField,
409
+ `${getStateOnlyField} must be passed as filters.${getStateOnlyField} for action=${normalizedAction}`,
410
+ );
411
+ }
412
+ }
413
+
414
+ const mergedFilters = {
415
+ ...filters,
416
+ ...(!Object.prototype.hasOwnProperty.call(filters, 'direction') && hasProvidedToolParam(params, 'direction')
417
+ ? { direction: params.direction }
418
+ : {}),
419
+ ...(normalizedAction === 'get_state'
420
+ ? Object.fromEntries(
421
+ MANAGE_CONVERSATION_GET_STATE_TARGET_FIELDS
422
+ .filter((fieldId) => (
423
+ !Object.prototype.hasOwnProperty.call(filters, fieldId)
424
+ && hasProvidedToolParam(params, fieldId)
425
+ ))
426
+ .map((fieldId) => [fieldId, params[fieldId]]),
427
+ )
428
+ : {}),
429
+ };
430
+ return normalizeChatInboxListFiltersInput({ filters: mergedFilters });
431
+ }
432
+
295
433
  function parseToolResultPayload(result = null) {
296
434
  const text = result?.content?.[0]?.text;
297
435
  if (typeof text !== 'string') return null;
@@ -924,10 +1062,27 @@ function createTerminalToolAdapters(api, plugin, internalTools) {
924
1062
  agentCode: stringParam({ description: 'Target public agent code for request.', minLength: 1 }),
925
1063
  openingMessage: stringParam({ description: 'Request/re-engagement kickoff message.', minLength: 1 }),
926
1064
  worldId: worldIdProperty,
927
- filters: objectParam({ description: 'List filters.', additionalProperties: true }),
928
- chatRequestId: stringParam({ description: 'Request id for accept/reject.', minLength: 1 }),
929
- conversationKey: stringParam({ description: 'Conversation key for get_state/close.', minLength: 1 }),
930
- localSessionKey: stringParam({ description: 'Local conversation session key for get_state/close.', minLength: 1 }),
1065
+ direction: stringParam({
1066
+ description: 'Top-level alias for filters.direction on action=list_related/get_state.',
1067
+ enumValues: CHAT_INBOX_FILTER_DIRECTIONS,
1068
+ examples: ['outbound'],
1069
+ }),
1070
+ filters: buildChatInboxFiltersParam({
1071
+ description: 'Inbox filters for action=list_related/get_state.',
1072
+ worldIdProperty,
1073
+ }),
1074
+ chatRequestId: stringParam({
1075
+ description: 'Request id for action=accept/reject, or a top-level get_state convenience target that normalizes to filters.chatRequestId.',
1076
+ minLength: 1,
1077
+ }),
1078
+ conversationKey: stringParam({
1079
+ description: 'Conversation key for action=close, or a top-level get_state convenience target that normalizes to filters.conversationKey.',
1080
+ minLength: 1,
1081
+ }),
1082
+ localSessionKey: stringParam({
1083
+ description: 'Local conversation session key for action=close, or a top-level get_state convenience target that normalizes to filters.localSessionKey.',
1084
+ minLength: 1,
1085
+ }),
931
1086
  },
932
1087
  }),
933
1088
  async execute(toolCallId, params = {}) {
@@ -939,10 +1094,19 @@ function createTerminalToolAdapters(api, plugin, internalTools) {
939
1094
  });
940
1095
  return rewriteToolResultName(result, manageConversationsTool, action);
941
1096
  }
942
- if (['list_related', 'get_state', 'accept', 'reject'].includes(action)) {
1097
+ if (action === 'list_related' || action === 'get_state') {
1098
+ const filters = normalizeManageConversationInboxQuery(params, action);
943
1099
  const result = await requireTerminalTool(internalTools, 'claworld_chat_inbox').execute(toolCallId, {
944
1100
  ...params,
945
- action: ['list_related', 'get_state'].includes(action) ? 'list' : action,
1101
+ action: 'list',
1102
+ ...(Object.keys(filters).length > 0 ? { filters } : {}),
1103
+ });
1104
+ return rewriteToolResultName(result, manageConversationsTool, action);
1105
+ }
1106
+ if (action === 'accept' || action === 'reject') {
1107
+ const result = await requireTerminalTool(internalTools, 'claworld_chat_inbox').execute(toolCallId, {
1108
+ ...params,
1109
+ action,
946
1110
  });
947
1111
  return rewriteToolResultName(result, manageConversationsTool, action);
948
1112
  }
@@ -1690,45 +1854,10 @@ function buildRegisteredTools(api, plugin) {
1690
1854
  examples: ['list', 'accept', 'reject'],
1691
1855
  }),
1692
1856
  filters: objectParam({
1693
- description: 'Optional list filters for query mode. Omit to review the full inbox across inbound and outbound items.',
1694
- properties: {
1695
- direction: stringParam({
1696
- description: 'Filter from the current account perspective.',
1697
- enumValues: CHAT_INBOX_FILTER_DIRECTIONS,
1698
- examples: ['outbound'],
1699
- }),
1700
- mode: stringParam({
1701
- description: 'Filter to direct or world-scoped chat items.',
1702
- enumValues: CHAT_INBOX_FILTER_MODES,
1703
- examples: ['world'],
1704
- }),
1705
- status: stringParam({
1706
- description: 'Filter to pending or terminal requests, or to chats by current status.',
1707
- enumValues: CHAT_INBOX_FILTER_STATUSES,
1708
- examples: ['active'],
1709
- }),
1710
- worldId: worldIdProperty,
1711
- chatRequestId: stringParam({
1712
- description: 'Filter to one canonical chat request id.',
1713
- minLength: 1,
1714
- examples: ['req_demo_1'],
1715
- }),
1716
- conversationKey: stringParam({
1717
- description: 'Filter to one canonical conversation key.',
1718
- minLength: 1,
1719
- examples: ['pair:agt_alice::agt_moza:world:dating-demo-world'],
1720
- }),
1721
- localSessionKey: stringParam({
1722
- description: 'Filter to one local Claworld session reference for internal tracking, summaries, or orchestration only. Not a transport address for sending a user message to the peer.',
1723
- minLength: 1,
1724
- examples: ['conversation:pair:agt_alice::agt_moza:world:dating-demo-world'],
1725
- }),
1726
- counterpartyAgentId: stringParam({
1727
- description: 'Filter to one counterparty agentId.',
1728
- minLength: 1,
1729
- examples: ['agt_alice'],
1730
- }),
1731
- },
1857
+ ...buildChatInboxFiltersParam({
1858
+ description: 'Optional list filters for query mode. Omit to review the full inbox across inbound and outbound items.',
1859
+ worldIdProperty,
1860
+ }),
1732
1861
  }),
1733
1862
  chatRequestId: stringParam({
1734
1863
  description: 'Canonical chat request id returned by claworld_chat_inbox pendingRequests. Required for action=accept or action=reject.',