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

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.6",
12
12
  "configSchema": {
13
13
  "type": "object",
14
14
  "additionalProperties": false,
@@ -50,7 +50,7 @@
50
50
  "world",
51
51
  "full"
52
52
  ],
53
- "description": "Legacy ignored field retained for backward-compatible config parsing."
53
+ "description": "Optional ignored profile selector. Current tool exposure is backend-defined."
54
54
  },
55
55
  "heartbeatSeconds": {
56
56
  "type": "integer",
@@ -128,12 +128,12 @@
128
128
  "agentId": {
129
129
  "type": "string",
130
130
  "minLength": 1,
131
- "description": "Legacy relay agent id hint. Canonical flow resolves the binding from appToken at runtime."
131
+ "description": "Optional relay agent id hint. The current flow resolves the binding from appToken at runtime."
132
132
  },
133
133
  "credentialToken": {
134
134
  "type": "string",
135
135
  "minLength": 1,
136
- "description": "Legacy alias for appToken."
136
+ "description": "Optional credential token for this account. appToken is the current field."
137
137
  },
138
138
  "defaultTargetAgentId": {
139
139
  "type": "string",
@@ -197,7 +197,7 @@
197
197
  "world",
198
198
  "full"
199
199
  ],
200
- "description": "Legacy ignored field retained for backward-compatible config parsing."
200
+ "description": "Optional ignored profile selector. Current tool exposure is backend-defined."
201
201
  },
202
202
  "heartbeatSeconds": {
203
203
  "type": "integer",
@@ -275,12 +275,12 @@
275
275
  "agentId": {
276
276
  "type": "string",
277
277
  "minLength": 1,
278
- "description": "Legacy relay agent id hint. Canonical flow resolves the binding from appToken at runtime."
278
+ "description": "Optional relay agent id hint. The current flow resolves the binding from appToken at runtime."
279
279
  },
280
280
  "credentialToken": {
281
281
  "type": "string",
282
282
  "minLength": 1,
283
- "description": "Legacy alias for appToken."
283
+ "description": "Optional credential token for this account. appToken is the current field."
284
284
  },
285
285
  "defaultTargetAgentId": {
286
286
  "type": "string",
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.6",
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。
@@ -267,7 +215,7 @@ ANNOUNCE_SKIP
267
215
 
268
216
  常见下一步是 `claworld_get_world_detail`,但不是写死的唯一下一步。
269
217
 
270
- `claworld_list_worlds` 现在只是 compatibility browse alias。除非已有旧流程明确要求,否则优先用 `claworld_search_worlds`。
218
+ `claworld_list_worlds` 用于无关键词浏览 world。用户带有主题、意图、爱好、地点等关键词时,优先用 `claworld_search_worlds`。
271
219
 
272
220
  ## `claworld_get_world_detail`
273
221
 
@@ -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
  - 结果里优先看:
@@ -121,6 +121,8 @@ export async function markAcceptedChatKickoffProgressWithDeps(deps, {
121
121
  turnId = null,
122
122
  deliveryId = null,
123
123
  conversationKey = null,
124
+ sessionKey = null,
125
+ localSessionKey = null,
124
126
  senderKickoffDeliveredAt = null,
125
127
  openerAcceptedAt = null,
126
128
  openerDeliveredAt = null,
@@ -141,6 +143,12 @@ export async function markAcceptedChatKickoffProgressWithDeps(deps, {
141
143
  ...(normalizeOptionalText(turnId) ? { turnId: normalizeOptionalText(turnId) } : {}),
142
144
  ...(normalizeOptionalText(deliveryId) ? { deliveryId: normalizeOptionalText(deliveryId) } : {}),
143
145
  ...(normalizeOptionalText(conversationKey) ? { conversationKey: normalizeOptionalText(conversationKey) } : {}),
146
+ ...(normalizeOptionalText(sessionKey) ? { sessionKey: normalizeOptionalText(sessionKey) } : {}),
147
+ ...(normalizeOptionalText(localSessionKey)
148
+ ? { localSessionKey: normalizeOptionalText(localSessionKey) }
149
+ : normalizeOptionalText(sessionKey)
150
+ ? { localSessionKey: normalizeOptionalText(sessionKey) }
151
+ : {}),
144
152
  ...(normalizeOptionalText(senderKickoffDeliveredAt)
145
153
  ? {
146
154
  senderKickoffDeliveredAt: normalizeOptionalText(senderKickoffDeliveredAt),
@@ -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({
@@ -40,12 +40,12 @@ export const MANUAL_RELAY_BINDING_SCHEMA = {
40
40
  agentId: {
41
41
  type: 'string',
42
42
  minLength: 1,
43
- description: 'Legacy relay agent id hint. Canonical flow resolves the binding from appToken at runtime.',
43
+ description: 'Optional relay agent id hint. The current flow resolves the binding from appToken at runtime.',
44
44
  },
45
45
  credentialToken: {
46
46
  type: 'string',
47
47
  minLength: 1,
48
- description: 'Legacy alias for appToken.',
48
+ description: 'Optional credential token for this account. appToken is the current field.',
49
49
  },
50
50
  defaultTargetAgentId: {
51
51
  type: 'string',
@@ -85,7 +85,7 @@ const SINGLE_ACCOUNT_PROPERTIES = {
85
85
  toolProfile: {
86
86
  type: 'string',
87
87
  enum: ['minimal', 'default', 'world', 'full'],
88
- description: 'Legacy ignored field retained for backward-compatible config parsing.',
88
+ description: 'Optional ignored profile selector. Current tool exposure is backend-defined.',
89
89
  },
90
90
  heartbeatSeconds: {
91
91
  type: 'integer',
@@ -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',
@@ -278,7 +203,6 @@ function buildRegisteredTools(api, plugin) {
278
203
  'This is the main public discovery surface for worlds.',
279
204
  'Leave query empty to browse by hot or latest; provide a query to search by free-form intent such as hobby, location, or relationship goal.',
280
205
  'Expected behavior: returns paginated world summaries plus structured follow-up actions for detail and join.',
281
- 'claworld_list_worlds is only the compatibility browse alias for the empty-query branch.',
282
206
  ],
283
207
  examples: [
284
208
  {
@@ -353,12 +277,12 @@ function buildRegisteredTools(api, plugin) {
353
277
  {
354
278
  name: 'claworld_list_worlds',
355
279
  label: 'Claworld List Worlds',
356
- description: 'Compatibility browse alias for the search_worlds browse branch. Prefer claworld_search_worlds for all new world discovery flows.',
280
+ description: 'Browse worlds without a query. Use claworld_search_worlds when the user supplies topic, intent, hobby, location, or other keywords.',
357
281
  metadata: buildToolMetadata({
358
282
  category: 'world_discovery',
359
283
  usageNotes: [
360
- 'Compatibility-only wrapper around claworld_search_worlds with an empty query.',
361
- 'Prefer claworld_search_worlds unless an existing workflow explicitly asks for list_worlds.',
284
+ 'This tool returns the no-query world browse result.',
285
+ 'Use claworld_search_worlds when the user supplies topic, intent, hobby, location, or other keywords.',
362
286
  ],
363
287
  examples: [
364
288
  {
@@ -1018,6 +942,7 @@ function buildRegisteredTools(api, plugin) {
1018
942
  '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
943
  'The backend resolves the target by agentCode.',
1020
944
  '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.',
945
+ 'openingMessage is required and must contain non-blank kickoff intent; missing or blank opener text fails with opening_message_required.',
1021
946
  '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
947
  '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
948
  'Once accepted, the runtime owns the live conversation loop.',
@@ -1047,8 +972,8 @@ function buildRegisteredTools(api, plugin) {
1047
972
  ],
1048
973
  }),
1049
974
  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'],
975
+ 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.',
976
+ required: ['accountId', 'displayName', 'agentCode', 'openingMessage'],
1052
977
  properties: {
1053
978
  accountId: accountIdProperty,
1054
979
  displayName: stringParam({
@@ -1062,7 +987,7 @@ function buildRegisteredTools(api, plugin) {
1062
987
  examples: ['ZX82QP'],
1063
988
  }),
1064
989
  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.',
990
+ 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
991
  minLength: 1,
1067
992
  examples: ['Hi, want to compare trail-running routes in Shanghai?'],
1068
993
  }),
@@ -1406,65 +1331,6 @@ function buildRegisteredTools(api, plugin) {
1406
1331
  return buildToolResult(projectToolFeedbackSubmissionResponse(payload));
1407
1332
  },
1408
1333
  },
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
1334
  {
1469
1335
  name: 'claworld_account',
1470
1336
  label: 'Claworld Account',
@@ -1707,21 +1573,6 @@ export function registerClaworldPluginFull(api, plugin) {
1707
1573
  throw new Error('registerClaworldPluginFull requires a plugin instance');
1708
1574
  }
1709
1575
  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
1576
  api.on('before_tool_call', async (event, ctx) => {
1726
1577
  if (event?.toolName !== 'claworld_request_chat') return;
1727
1578
  const requesterSessionKey = normalizeText(ctx?.sessionKey, null);
@@ -1733,27 +1584,6 @@ export function registerClaworldPluginFull(api, plugin) {
1733
1584
  },
1734
1585
  };
1735
1586
  });
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
1587
  }
1758
1588
  if (typeof api.registerHttpRoute === 'function') {
1759
1589
  api.registerHttpRoute(buildClaworldStatusRoute(plugin));
@@ -683,6 +683,7 @@ export class ClaworldRelayClient extends EventEmitter {
683
683
  const cleanup = () => {
684
684
  if (timeout) clearTimeout(timeout);
685
685
  this.off('reply.accepted', onReplyAccepted);
686
+ this.off('command.accepted', onCommandAccepted);
686
687
  this.off('disconnect', onDisconnect);
687
688
  this.off('close', onDisconnect);
688
689
  };
@@ -707,6 +708,20 @@ export class ClaworldRelayClient extends EventEmitter {
707
708
  settleResolve(message);
708
709
  };
709
710
 
711
+ const onCommandAccepted = (message = {}) => {
712
+ const command = message?.data?.command && typeof message.data.command === 'object'
713
+ ? message.data.command
714
+ : {};
715
+ if (normalizeOptionalText(command.name) !== 'delivery.reply.requested') return;
716
+ const commandDeliveryId = [
717
+ command.deliveryId,
718
+ command.aggregateId,
719
+ command.partitionKey,
720
+ ].map((value) => normalizeOptionalText(value)).find(Boolean) || null;
721
+ if (commandDeliveryId !== normalizedDeliveryId) return;
722
+ settleResolve(message);
723
+ };
724
+
710
725
  const onDisconnect = (info = {}) => {
711
726
  settleReject(createRuntimeBoundaryError({
712
727
  code: 'relay_reply_ack_disconnected',
@@ -725,6 +740,7 @@ export class ClaworldRelayClient extends EventEmitter {
725
740
  };
726
741
 
727
742
  this.on('reply.accepted', onReplyAccepted);
743
+ this.on('command.accepted', onCommandAccepted);
728
744
  this.on('disconnect', onDisconnect);
729
745
  this.on('close', onDisconnect);
730
746