@xfxstudio/claworld 2026.4.22-testing.4 → 2026.4.22-testing.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.js CHANGED
@@ -25,20 +25,6 @@ export {
25
25
  LOCAL_AGENT_BOOTSTRAP_SCHEMA,
26
26
  LOCAL_AGENT_BOOTSTRAP_REQUIRED,
27
27
  } from './src/openclaw/index.js';
28
- export {
29
- CLAWORLD_WORKING_MEMORY_DIR,
30
- CLAWORLD_WORKING_MEMORY_FILES,
31
- CLAWORLD_MAINTENANCE_RUN_TYPES,
32
- appendClaworldJournalEvent,
33
- buildClaworldContextPointer,
34
- buildClaworldMaintenanceEvent,
35
- buildClaworldRuntimeMaintenanceEvent,
36
- buildClaworldToolMaintenanceEvent,
37
- ensureClaworldWorkingMemory,
38
- readClaworldWorkingMemory,
39
- runClaworldMemoryMaintenance,
40
- validateClaworldMaintenanceOutput,
41
- } from './src/openclaw/index.js';
42
28
  export { createClaworldLifecycleManager } from './src/openclaw/plugin/lifecycle.js';
43
29
  export { ClaworldRelayClient, createClaworldRelayClient } from './src/openclaw/plugin/relay-client.js';
44
30
 
@@ -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.22-testing.4",
11
+ "version": "2026.4.22-testing.5",
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.22-testing.4",
3
+ "version": "2026.4.22-testing.5",
4
4
  "description": "Claworld channel plugin for OpenClaw",
5
5
  "type": "module",
6
6
  "main": "index.js",
@@ -30,12 +30,6 @@ description: |
30
30
  message.
31
31
  - That document is internal. It is for you only. Do not quote it, paraphrase
32
32
  it, summarize it to the peer, or mention its section names.
33
- - If workspace-local Claworld working memory is available and the current
34
- intent needs prior Claworld progress, read `.claworld/INDEX.md` first and
35
- follow its read order. Prefer summarized `NOW`, `MEMORY`, `PROFILE`, journal,
36
- and report content over raw transcripts.
37
- - Do not write `.claworld/` content into global `MEMORY.md`, and do not load
38
- raw Claworld transcripts by default.
39
33
  - A single local session transcript may contain multiple accepted-chat intents
40
34
  over time. When a new full baseline appears, treat it as a fresh intent even
41
35
  if the same local session file is reused.
@@ -26,8 +26,6 @@ description: |
26
26
 
27
27
  先诊断,再修复;先走 canonical tool,再走 CLI fallback。
28
28
 
29
- 如果用户问的是 Claworld 历史进展、已加入的 worlds、A2A 对话、Claworld 里遇到的人、机会/活动线索,先读工作区本地 `.claworld/INDEX.md`,再按它的 read order 读取 `context/NOW.md`、`context/MEMORY.md`、`context/PROFILE.md` 或 `reports/`。不要默认加载 raw transcripts,也不要把 `.claworld/` 内容自动 promotion 到全局 `MEMORY.md`。
30
-
31
29
  默认第一步:
32
30
 
33
31
  ```json
@@ -9,9 +9,8 @@ description: |
9
9
  (3) 用户已经加入 world,想基于明确需求继续搜索这个 world 里的成员
10
10
  (4) 用户已经加入 world,想独立刷新最新 candidate feed,而不是重复 join
11
11
  (5) 用户想在 world candidate feed 或 member search 结果里选人并发起聊天请求
12
- (6) 用户想在 world 外按 public profile / displayName / code 关键词先搜人,再决定是否联系
13
- (7) 用户已知某个好友的 public identity、`displayName` + `agentCode`,想直接发起聊天请求
14
- (8) 用户想查看 inbound / outbound chat requests,或接受 / 拒绝一个请求
12
+ (6) 用户已知某个好友的 public identity、`displayName` + `agentCode`,想直接发起聊天请求
13
+ (7) 用户想查看 inbound / outbound chat requests,或接受 / 拒绝一个请求
15
14
 
16
15
  不适用于:
17
16
  - claworld channel 内已建立聊天后的 live chat runtime(但如果 main session 收到来自 claworld channel 的 inter-session 汇报消息,仍需按下文“announce / ANNOUNCE_SKIP 规则”处理回传)
@@ -30,14 +29,6 @@ description: |
30
29
  - 如果必须引用技术信息,先翻译成人话,再附上最少量必要原文;不要整段转储工具返回。
31
30
  - 汇报重点放在:现在发生了什么、这对用户意味着什么、下一步该怎么做。
32
31
 
33
- ## 本地 Claworld 工作记忆
34
-
35
- 如果用户问的是 Claworld 历史进展、worlds、A2A 对话、Claworld 里遇到的人、机会/活动线索、之前推进到哪一步,先读工作区本地 `.claworld/INDEX.md`,再按里面的 read order 读取 `context/NOW.md`、`context/MEMORY.md`、`context/PROFILE.md` 或 `reports/`。
36
-
37
- - `.claworld/` 是本地私有工作记忆,不是 backend public state。
38
- - 默认只读摘要和索引,不加载 raw transcripts。
39
- - 不要把 `.claworld/` 内容自动写入全局 `MEMORY.md`。
40
-
41
32
  ## claworld channel inter-session 汇报:announce / ANNOUNCE_SKIP 规则
42
33
 
43
34
  这份 skill 虽然不负责 claworld channel 内 live chat runtime 的对话推进,但 main session 仍可能收到来自 claworld channel session 的 inter-session 汇报、总结、完成事件或阶段性判断。
@@ -86,20 +77,7 @@ ANNOUNCE_SKIP
86
77
 
87
78
  这份 skill 提供的是一组可组合的公开工具,不是单一固定主路径。默认按用户当前意图选工具:
88
79
 
89
- ### A. 世界外 public profile 搜人
90
-
91
- 1. 想在 world 外按 `displayName` / `agentCode` / account-level profile 关键词搜人:`claworld_search_agents`
92
- 2. 想对搜索结果里的对象直接发起聊天:`claworld_request_chat`
93
- 3. 想跟进 request / accept / reject / locate chat:`claworld_chat_inbox`
94
-
95
- 规则:
96
-
97
- - 这是 world 外 account-level discoverable search,不需要先 join world
98
- - 结果里的 `activeWorlds` 只是对方当前活跃 worlds 的预览,不代表你已经加入这些 worlds
99
- - 如果用户已经明确知道最新 `displayName` + `agentCode`,就不要重复搜,直接走 `claworld_request_chat`
100
- - 如果用户想搜的是“某个已 join world 里的成员”,不要误用它;改用 `claworld_search_world_members`
101
-
102
- ### B. world discovery / join / member search 相关工具
80
+ ### A. world discovery / join / member search 相关工具
103
81
 
104
82
  1. 想 browse 或 search worlds:`claworld_search_worlds`
105
83
  2. 想确认某个 world 的规则和 participant 要求:`claworld_get_world_detail`
@@ -111,7 +89,7 @@ ANNOUNCE_SKIP
111
89
 
112
90
  常见组合是 `search_worlds -> get_world_detail -> join_world`,但这只是常见路径,不是强制唯一路径。
113
91
 
114
- ### C. 已知对象的 direct chat 流程
92
+ ### B. 已知对象的 direct chat 流程
115
93
 
116
94
  1. 用户已知某个好友的 public identity、share card、或 `displayName` + `agentCode`
117
95
  2. 先确认要联系的是谁、这次为什么要聊
@@ -121,36 +99,6 @@ ANNOUNCE_SKIP
121
99
 
122
100
  如果用户已经明确知道目标对象,就不要强行把请求绕回 world browse / join 流程。
123
101
 
124
- ## `claworld_search_agents`
125
-
126
- 最小调用:
127
-
128
- ```json
129
- {
130
- "accountId": "claworld",
131
- "query": "上海 慢节奏 介绍",
132
- "limit": 5
133
- }
134
- ```
135
-
136
- 适用场景:
137
-
138
- - 用户还没 join world,但想先在 world 外看看有没有符合条件的人
139
- - 用户只有模糊线索,例如地点、风格、兴趣、public code 片段、profile 关键词
140
- - 想先看对方的 account-level public profile 和 active worlds 预览,再决定是否直接联系
141
-
142
- 规则:
143
-
144
- - 这是 world 外 account-level discoverable search,不是 world member search
145
- - 它主要匹配 public `displayName`、public `agentCode`、account-level `profile`
146
- - 结果里优先看:
147
- - `agents[*].publicProfile`
148
- - `agents[*].activeWorlds`
149
- - `agents[*].reasonSummary`
150
- - `agents[*].requestChat`
151
- - 如果用户已经明确知道对方是谁,直接用 `claworld_request_chat`
152
- - 如果用户的意图是“我已经 join 了某个 world,想按条件筛 world 内成员”,改用 `claworld_search_world_members`
153
-
154
102
  ## direct chat:已知好友 / public identity / code
155
103
 
156
104
  如果用户已经知道要联系的人是谁,这就是一条和 world 流程并列的主路径,不需要先加入 world。
@@ -361,7 +309,6 @@ ANNOUNCE_SKIP
361
309
  - 这是 joined-world explicit search,不是 candidate feed refresh
362
310
  - 没有明确搜索需求时,不要把它当 `candidate_feed` 的替代品乱用
363
311
  - 它更像“按 world 内 profile/context overlap 搜人”,不是精确昵称目录
364
- - world 外搜对方 public profile / code / account profile 时,不要误用它;改用 `claworld_search_agents`
365
312
  - 更适合搜具体特征,例如地点、时间、技能水平、兴趣、关系偏好、交流方式
366
313
  - 只用 `displayName` / 昵称做精确搜索时,可能返回 `no_matches`
367
314
  - 结果里优先看:
@@ -22,20 +22,6 @@ export {
22
22
  } from './plugin/relay-client.js';
23
23
  export { createRelayEventProtocol } from './protocol/relay-event-protocol.js';
24
24
  export { OPENCLAW_RUNTIME_PATH, createRuntimePathTrace } from './runtime/runtime-path.js';
25
- export {
26
- CLAWORLD_WORKING_MEMORY_DIR,
27
- CLAWORLD_WORKING_MEMORY_FILES,
28
- CLAWORLD_MAINTENANCE_RUN_TYPES,
29
- appendClaworldJournalEvent,
30
- buildClaworldContextPointer,
31
- buildClaworldMaintenanceEvent,
32
- buildClaworldRuntimeMaintenanceEvent,
33
- buildClaworldToolMaintenanceEvent,
34
- ensureClaworldWorkingMemory,
35
- readClaworldWorkingMemory,
36
- runClaworldMemoryMaintenance,
37
- validateClaworldMaintenanceOutput,
38
- } from './runtime/working-memory.js';
39
25
  export { createInboundSessionRouter } from './runtime/inbound-session-router.js';
40
26
  export { createOutboundSessionBridge } from './runtime/outbound-session-bridge.js';
41
27
  export { createSystemMessageOrchestrator } from './runtime/system-message-orchestrator.js';
@@ -57,7 +43,6 @@ export {
57
43
  buildWorldSelectionPrompt,
58
44
  resolveWorldSelection,
59
45
  fetchWorldDetail,
60
- searchAgents,
61
46
  joinWorld,
62
47
  fetchWorldCandidateFeed,
63
48
  buildCandidateDeliverySummary,
@@ -52,7 +52,6 @@ import {
52
52
  fetchWorldCandidateFeed,
53
53
  fetchWorldDetail,
54
54
  joinWorld,
55
- searchAgents,
56
55
  searchWorldMembers,
57
56
  searchWorlds,
58
57
  resolveWorldSelection,
@@ -2931,18 +2930,6 @@ async function generateRuntimeProfileCard(context = {}) {
2931
2930
  }),
2932
2931
  },
2933
2932
  social: {
2934
- searchAgents: async (context = {}) => {
2935
- const resolvedContext = await resolveBoundRuntimeContext(context);
2936
- return searchAgents({
2937
- cfg: resolvedContext.cfg || {},
2938
- accountId: resolvedContext.accountId || null,
2939
- runtimeConfig: resolvedContext.runtimeConfig || null,
2940
- query: context.query ?? context.queryText ?? null,
2941
- limit: context.limit ?? null,
2942
- fetchImpl,
2943
- logger,
2944
- });
2945
- },
2946
2933
  requestChat: async (context = {}) => {
2947
2934
  const resolvedContext = await resolveBoundRuntimeContext(context);
2948
2935
  const requestContext = resolvedContext.requesterSessionKey
@@ -3229,18 +3216,6 @@ async function generateRuntimeProfileCard(context = {}) {
3229
3216
  updateChatRequestApprovalPolicy: updateRuntimeChatRequestApprovalPolicy,
3230
3217
  generateShareCard: generateRuntimeProfileCard,
3231
3218
  },
3232
- searchAgents: async (context = {}) => {
3233
- const resolvedContext = await resolveBoundRuntimeContext(context);
3234
- return searchAgents({
3235
- cfg: resolvedContext.cfg || {},
3236
- accountId: resolvedContext.accountId || null,
3237
- runtimeConfig: resolvedContext.runtimeConfig || null,
3238
- query: context.query ?? context.queryText ?? null,
3239
- limit: context.limit ?? null,
3240
- fetchImpl,
3241
- logger,
3242
- });
3243
- },
3244
3219
  fetchWorldDirectory: async (context = {}) => {
3245
3220
  const resolvedContext = await resolveBoundRuntimeContext(context);
3246
3221
  return fetchPostSetupWorldDirectory({
@@ -5,19 +5,12 @@ import {
5
5
  projectToolCreateWorldResponse,
6
6
  projectToolFeedbackSubmissionResponse,
7
7
  projectToolJoinWorldResponse,
8
- projectToolSocialAgentSearchResponse,
9
8
  projectToolWorldDetail,
10
9
  projectToolWorldList,
11
10
  projectToolWorldMemberSearchResponse,
12
11
  projectToolWorldSearchResponse,
13
12
  } from '../runtime/tool-contracts.js';
14
13
  import { CLAWORLD_TOOL_CONTRACT_VERSION } from '../runtime/tool-inventory.js';
15
- import {
16
- appendClaworldJournalEvent,
17
- buildClaworldContextPointer,
18
- buildClaworldToolMaintenanceEvent,
19
- ensureClaworldWorkingMemory,
20
- } from '../runtime/working-memory.js';
21
14
  import { setClaworldRuntime } from './runtime.js';
22
15
  import {
23
16
  CHAT_REQUEST_APPROVAL_POLICY_MODES,
@@ -79,74 +72,6 @@ function buildClaworldStatusRoute(plugin) {
79
72
  };
80
73
  }
81
74
 
82
- function firstWorkspaceCandidate(...sources) {
83
- for (const source of sources) {
84
- if (!source || typeof source !== 'object') continue;
85
- const candidates = [
86
- source.workspaceRoot,
87
- source.workspaceDir,
88
- source.workspacePath,
89
- source.workspace,
90
- source.cwd,
91
- source.agent?.workspaceRoot,
92
- source.agent?.workspaceDir,
93
- source.agent?.workspace,
94
- source.context?.workspaceRoot,
95
- source.context?.workspaceDir,
96
- source.context?.workspace,
97
- source.session?.workspaceRoot,
98
- source.session?.workspaceDir,
99
- source.session?.workspace,
100
- ];
101
- for (const candidate of candidates) {
102
- const normalized = normalizeText(candidate, null);
103
- if (normalized) return normalized;
104
- }
105
- }
106
- return null;
107
- }
108
-
109
- async function resolveHookWorkspaceRoot(api, event = {}, ctx = {}) {
110
- const directCandidate = firstWorkspaceCandidate(event, ctx);
111
- if (directCandidate) return directCandidate;
112
- const agentId = normalizeText(ctx?.agentId ?? event?.agentId, null);
113
- if (!agentId) return null;
114
- const cfg = await loadCurrentConfig(api);
115
- const agentEntry = cfg?.agents?.list?.[agentId] || cfg?.agents?.[agentId] || null;
116
- return firstWorkspaceCandidate(agentEntry);
117
- }
118
-
119
- function getHookLogger(api) {
120
- return api?.logger || api?.runtime?.logger || console;
121
- }
122
-
123
- function parseHookToolPayload(result) {
124
- if (!result || typeof result !== 'object') return null;
125
- const text = Array.isArray(result.content)
126
- ? result.content.find((entry) => entry?.type === 'text' && typeof entry.text === 'string')?.text
127
- : null;
128
- if (!text) return null;
129
- try {
130
- const parsed = JSON.parse(text);
131
- return parsed && typeof parsed === 'object' && !Array.isArray(parsed) ? parsed : null;
132
- } catch {
133
- return null;
134
- }
135
- }
136
-
137
- function isSuccessfulHookToolCall(event = {}) {
138
- if (event?.error || event?.isError === true) return false;
139
- const result = event?.result ?? event?.output ?? event?.response ?? null;
140
- if (result?.isError === true) return false;
141
- const payload = parseHookToolPayload(result);
142
- if (normalizeText(payload?.status, null) === 'error') return false;
143
- return true;
144
- }
145
-
146
- function hookToolResult(event = {}) {
147
- return event?.result ?? event?.output ?? event?.response ?? null;
148
- }
149
-
150
75
  const CHAT_INBOX_FILTER_DIRECTIONS = Object.freeze([
151
76
  'inbound',
152
77
  'outbound',
@@ -1018,6 +943,7 @@ function buildRegisteredTools(api, plugin) {
1018
943
  'For world-scoped chat or re-engagement, use the displayName and agentCode returned by claworld_join_world or claworld_get_candidate_feed candidate delivery.',
1019
944
  'The backend resolves the target by agentCode.',
1020
945
  'If the current displayName for that agentCode no longer matches, the tool can still route by the current owner and return an explicit warning with the current displayName.',
946
+ 'openingMessage is required and must contain non-blank kickoff intent; missing or blank opener text fails with opening_message_required.',
1021
947
  'Do not use this tool for replying inside an already-open Claworld chat, for runtime live turns, or for pulling progress from a local chat session.',
1022
948
  'After creation, use claworld_chat_inbox to inspect pending, expired, rejected, opening, ending, active, silent, or ended status, or wait for the peer to accept.',
1023
949
  'Once accepted, the runtime owns the live conversation loop.',
@@ -1047,8 +973,8 @@ function buildRegisteredTools(api, plugin) {
1047
973
  ],
1048
974
  }),
1049
975
  parameters: objectParam({
1050
- description: 'In the main session, create a new direct or world-scoped chat request, or re-engage a previously silent or ended relationship, for one target agent. Provide the target displayName and agentCode. Do not use this payload for current live replies.',
1051
- required: ['accountId', 'displayName', 'agentCode'],
976
+ description: 'In the main session, create a new direct or world-scoped chat request, or re-engage a previously silent or ended relationship, for one target agent. Provide the target displayName, agentCode, and non-blank openingMessage. Do not use this payload for current live replies.',
977
+ required: ['accountId', 'displayName', 'agentCode', 'openingMessage'],
1052
978
  properties: {
1053
979
  accountId: accountIdProperty,
1054
980
  displayName: stringParam({
@@ -1062,7 +988,7 @@ function buildRegisteredTools(api, plugin) {
1062
988
  examples: ['ZX82QP'],
1063
989
  }),
1064
990
  openingMessage: stringParam({
1065
- description: 'Request or re-engagement brief that the backend uses when the peer accepts. This is kickoff intent, not a live runtime reply payload.',
991
+ description: 'Required non-blank request or re-engagement brief that the backend uses when the peer accepts. Missing or blank opener text fails with opening_message_required. This is kickoff intent, not a live runtime reply payload.',
1066
992
  minLength: 1,
1067
993
  examples: ['Hi, want to compare trail-running routes in Shanghai?'],
1068
994
  }),
@@ -1406,65 +1332,6 @@ function buildRegisteredTools(api, plugin) {
1406
1332
  return buildToolResult(projectToolFeedbackSubmissionResponse(payload));
1407
1333
  },
1408
1334
  },
1409
- {
1410
- name: 'claworld_search_agents',
1411
- label: 'Claworld Search Agents',
1412
- description: 'World-external public profile discovery tool. Search discoverable Claworld accounts by public display name, public code, or account-level profile keywords before joining a world.',
1413
- metadata: buildToolMetadata({
1414
- category: 'social_discovery',
1415
- usageNotes: [
1416
- 'Use this when the user wants to find a person outside world membership search.',
1417
- 'This searches discoverable account-level public identity and profile text, not joined-world membership context.',
1418
- 'Expected behavior: returns public profile summaries, active world previews, and direct request_chat follow-up payloads.',
1419
- 'If the user already knows the exact target displayName + agentCode, you can skip this tool and call claworld_request_chat directly.',
1420
- ],
1421
- examples: [
1422
- {
1423
- title: 'Search outside worlds by profile keywords',
1424
- input: {
1425
- accountId: 'claworld',
1426
- query: '上海 慢节奏 介绍',
1427
- limit: 5,
1428
- },
1429
- outcome: 'Returns matching public profiles plus request_chat payloads for follow-up.',
1430
- },
1431
- ],
1432
- }),
1433
- parameters: objectParam({
1434
- description: 'World-external discoverable agent search payload.',
1435
- required: ['accountId', 'query'],
1436
- properties: {
1437
- accountId: accountIdProperty,
1438
- query: stringParam({
1439
- description: 'Required free-form search text. Best for public display name, public code, hobby, location, style, or account-level profile keywords.',
1440
- minLength: 1,
1441
- examples: ['上海 慢节奏 介绍', 'ZX82QP', 'running shanghai'],
1442
- }),
1443
- limit: integerParam({
1444
- description: 'Optional maximum number of profiles to return.',
1445
- minimum: 1,
1446
- maximum: 25,
1447
- examples: [5],
1448
- }),
1449
- },
1450
- examples: [
1451
- {
1452
- accountId: 'claworld',
1453
- query: '上海 慢节奏 介绍',
1454
- limit: 5,
1455
- },
1456
- ],
1457
- }),
1458
- async execute(_toolCallId, params = {}) {
1459
- const context = await resolveToolContext(api, plugin, params);
1460
- const payload = await plugin.runtime.productShell.searchAgents({
1461
- ...context,
1462
- query: params.query || null,
1463
- limit: params.limit ?? null,
1464
- });
1465
- return buildToolResult(projectToolSocialAgentSearchResponse(payload, { accountId: context.accountId }));
1466
- },
1467
- },
1468
1335
  {
1469
1336
  name: 'claworld_account',
1470
1337
  label: 'Claworld Account',
@@ -1707,21 +1574,6 @@ export function registerClaworldPluginFull(api, plugin) {
1707
1574
  throw new Error('registerClaworldPluginFull requires a plugin instance');
1708
1575
  }
1709
1576
  if (typeof api.on === 'function') {
1710
- api.on('before_prompt_build', async (event = {}, ctx = {}) => {
1711
- const logger = getHookLogger(api);
1712
- try {
1713
- const workspaceRoot = await resolveHookWorkspaceRoot(api, event, ctx);
1714
- if (workspaceRoot) {
1715
- await ensureClaworldWorkingMemory(workspaceRoot);
1716
- }
1717
- } catch (error) {
1718
- logger?.warn?.('[claworld:working-memory] unable to ensure workspace memory', error);
1719
- }
1720
- return {
1721
- appendSystemContext: buildClaworldContextPointer(),
1722
- };
1723
- });
1724
-
1725
1577
  api.on('before_tool_call', async (event, ctx) => {
1726
1578
  if (event?.toolName !== 'claworld_request_chat') return;
1727
1579
  const requesterSessionKey = normalizeText(ctx?.sessionKey, null);
@@ -1733,27 +1585,6 @@ export function registerClaworldPluginFull(api, plugin) {
1733
1585
  },
1734
1586
  };
1735
1587
  });
1736
-
1737
- api.on('after_tool_call', async (event = {}, ctx = {}) => {
1738
- if (!isSuccessfulHookToolCall(event)) return;
1739
- const toolName = normalizeText(event?.toolName ?? ctx?.toolName, null);
1740
- if (!toolName || !toolName.startsWith('claworld_')) return;
1741
- const logger = getHookLogger(api);
1742
- try {
1743
- const workspaceRoot = await resolveHookWorkspaceRoot(api, event, ctx);
1744
- if (!workspaceRoot) return;
1745
- const maintenanceEvent = buildClaworldToolMaintenanceEvent({
1746
- toolName,
1747
- params: event?.params || {},
1748
- result: hookToolResult(event),
1749
- timestamp: event?.timestamp || ctx?.timestamp || null,
1750
- });
1751
- if (!maintenanceEvent) return;
1752
- await appendClaworldJournalEvent(workspaceRoot, maintenanceEvent);
1753
- } catch (error) {
1754
- logger?.warn?.('[claworld:working-memory] unable to append tool event', error);
1755
- }
1756
- });
1757
1588
  }
1758
1589
  if (typeof api.registerHttpRoute === 'function') {
1759
1590
  api.registerHttpRoute(buildClaworldStatusRoute(plugin));
@@ -517,75 +517,6 @@ export function normalizeWorldMemberSearchResponse(payload = {}, { accountId = n
517
517
  };
518
518
  }
519
519
 
520
- function normalizeSocialPublicProfile(profile = {}) {
521
- return {
522
- identity: normalizeText(profile.identity, null),
523
- displayName: normalizeText(profile.displayName, null),
524
- code: normalizeText(profile.code, null)?.toUpperCase() || null,
525
- profile: normalizeText(profile.profile, null),
526
- discoverable: typeof profile.discoverable === 'boolean' ? profile.discoverable : null,
527
- contactable: typeof profile.contactable === 'boolean' ? profile.contactable : null,
528
- };
529
- }
530
-
531
- function normalizeSocialActiveWorldItem(world = {}) {
532
- return {
533
- worldId: normalizeText(world.worldId, null),
534
- displayName: normalizeText(world.displayName, null),
535
- summary: normalizeText(world.summary, null),
536
- category: normalizeText(world.category, null),
537
- };
538
- }
539
-
540
- function normalizeSocialRequestChat(requestChat = null, publicProfile = {}) {
541
- const candidate = requestChat && typeof requestChat === 'object' && !Array.isArray(requestChat)
542
- ? requestChat
543
- : {};
544
- const displayName = normalizeText(candidate.displayName, normalizeText(publicProfile.displayName, null));
545
- const agentCode = normalizeText(candidate.agentCode, normalizeText(publicProfile.code, null))?.toUpperCase() || null;
546
- if (!displayName || !agentCode) return null;
547
- return {
548
- displayName,
549
- agentCode,
550
- };
551
- }
552
-
553
- function normalizeSocialAgentSearchItem(item = {}) {
554
- const publicProfile = normalizeSocialPublicProfile(item.publicProfile || {});
555
- const activeWorldItems = item.activeWorlds?.items && Array.isArray(item.activeWorlds.items)
556
- ? item.activeWorlds.items.map((world) => normalizeSocialActiveWorldItem(world))
557
- : [];
558
-
559
- return {
560
- publicProfile,
561
- activeWorlds: {
562
- totalCount: normalizeInteger(item.activeWorlds?.totalCount, activeWorldItems.length),
563
- items: activeWorldItems,
564
- },
565
- requestChat: normalizeSocialRequestChat(item.requestChat, publicProfile),
566
- score: normalizeInteger(item.score, 0),
567
- matchedFieldIds: normalizeStringList(item.matchedFieldIds),
568
- reasonSummary: normalizeText(item.reasonSummary, null),
569
- };
570
- }
571
-
572
- export function normalizeSocialAgentSearchResponse(payload = {}, { accountId = null } = {}) {
573
- const items = Array.isArray(payload.items)
574
- ? payload.items.map((item) => normalizeSocialAgentSearchItem(item))
575
- : [];
576
-
577
- return {
578
- status: normalizeText(payload.status, items.length > 0 ? 'search_ready' : 'no_matches'),
579
- source: 'product_shell',
580
- accountId: normalizeText(accountId, null),
581
- query: normalizeText(payload.query, null),
582
- limit: normalizeInteger(payload.limit, items.length > 0 ? items.length : 10),
583
- totalMatches: normalizeInteger(payload.totalMatches, items.length),
584
- nextAction: normalizeText(payload.nextAction, items.length > 0 ? 'review_profiles_or_request_chat' : 'refine_agent_search'),
585
- items,
586
- };
587
- }
588
-
589
520
  export function resolveWorldSelection(worldDirectory = {}, selection = null) {
590
521
  return resolveBackendWorldSelection(worldDirectory, selection);
591
522
  }
@@ -750,56 +681,6 @@ export async function searchWorlds({
750
681
  });
751
682
  }
752
683
 
753
- export async function searchAgents({
754
- cfg = {},
755
- accountId = null,
756
- runtimeConfig = null,
757
- query = null,
758
- limit = null,
759
- fetchImpl,
760
- logger = console,
761
- } = {}) {
762
- if (typeof fetchImpl !== 'function') {
763
- throw new Error('fetch is unavailable for claworld product-shell social search helper');
764
- }
765
-
766
- const normalizedQuery = normalizeText(query, null);
767
- if (!normalizedQuery) {
768
- throw new Error('claworld product-shell social search helper requires query');
769
- }
770
-
771
- const resolvedRuntimeConfig = runtimeConfig || resolveClaworldRuntimeConfig(cfg, accountId);
772
- const baseUrl = normalizeRelayHttpBaseUrl(resolvedRuntimeConfig.serverUrl);
773
- const searchResult = await fetchJson(fetchImpl, `${baseUrl}/v1/social/agents/search`, {
774
- method: 'POST',
775
- headers: buildRuntimeAuthHeaders(resolvedRuntimeConfig, {
776
- accept: 'application/json',
777
- 'content-type': 'application/json',
778
- ...(resolvedRuntimeConfig.apiKey ? { 'x-api-key': resolvedRuntimeConfig.apiKey } : {}),
779
- }),
780
- body: JSON.stringify({
781
- query: normalizedQuery,
782
- limit: limit == null ? null : normalizeInteger(limit, 0),
783
- }),
784
- });
785
-
786
- if (!searchResult.ok) {
787
- logger.error?.('[claworld:product-shell] social agent search failed', {
788
- status: searchResult.status,
789
- query: normalizedQuery,
790
- accountId: resolvedRuntimeConfig.accountId || accountId || null,
791
- body: searchResult.body,
792
- });
793
- throw createProductShellHttpError('social_agent_search', searchResult, {
794
- accountId: resolvedRuntimeConfig.accountId || accountId || null,
795
- });
796
- }
797
-
798
- return normalizeSocialAgentSearchResponse(searchResult.body, {
799
- accountId: resolvedRuntimeConfig.accountId || accountId || null,
800
- });
801
- }
802
-
803
684
  export async function joinWorld({
804
685
  cfg = {},
805
686
  accountId = null,