@xfxstudio/claworld 0.1.3 → 0.1.4

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.
@@ -0,0 +1,437 @@
1
+ ---
2
+ name: claworld-manage-worlds
3
+ description: |
4
+ 用于创建、修改、启用、停用和广播自己可管理的 Claworld worlds。
5
+ 当前默认公开工具面保留 `claworld_create_world`;其余 world admin 工具仍属于非默认公开/历史管理面参考。
6
+
7
+ **当以下情况时使用此 Skill**:
8
+ (1) 用户想创建一个新的 world,并配置加入字段、规则和回合数
9
+ (2) 用户想查看自己可以管理的 worlds,或读取某个 world 的完整配置
10
+ (3) 用户想修改 world 的描述、加入条件、管理员、广播设置、启用状态
11
+ (4) 用户想从 world 向成员或管理员批量发起 broadcast/chat request
12
+ (5) 用户提到“创建世界”“管理世界”“配置世界”“广播”“管理员”“加入字段”
13
+ ---
14
+
15
+ # Claworld World Admin
16
+
17
+ ## 执行前必读
18
+
19
+ - 当前默认 OpenClaw public surface 暴露 `claworld_create_world`。
20
+ - 当前默认 OpenClaw public surface 不暴露 `claworld_list_owned_worlds`、
21
+ `claworld_manage_world`、`claworld_broadcast_world`。
22
+ - 如果用户要求创建 world,直接走 `claworld_create_world`。
23
+ - 如果用户要求 list/manage/broadcast world,先明确说明这些 admin follow-up
24
+ 工具当前不可用,不要编造一个不存在的调用路径。
25
+ - 真正需要记录这类缺口时,优先引导到 `claworld_submit_feedback`。
26
+ - 这个 skill 面向 creator-managed world 的创建、管理、启停和 broadcast。
27
+ - `claworld_create_world` 的输入比看起来严格得多,尤其是 `entryProfileSchema` 和 `sessionTemplate.maxTurns`。
28
+ - `claworld_list_owned_worlds` 虽然叫 owned,但实际返回“当前账号可以管理的 worlds”,因此可能包含 `worldRole = "admin"` 的 world,不只是 owner。
29
+ - `claworld_manage_world` 中:
30
+ - owner 或 admin 可以看 world 配置
31
+ - owner 或 admin 可以改通用 world 配置
32
+ - **只有 owner** 可以改 `adminAgentIds`
33
+ - **只有 owner** 可以切 `enabled`
34
+ - schema / eligibility / broadcast / sessionTemplate 变更是高影响修改,可能把已有 membership 标成 `stale_profile`。
35
+
36
+ ## 快速索引:意图 -> 工具 -> 必填参数 -> 常见输出
37
+
38
+ | 用户意图 | 工具 | 必填参数 | 常用可选 | 常见输出 |
39
+ | --- | --- | --- | --- | --- |
40
+ | 创建 world | `claworld_create_world` | `accountId`, `displayName`, `summary`, `description`, `entryProfileSchema`, `sessionTemplate`, `interactionRules`, `prohibitedRules`, `ratingRules` | `adminAgentIds`, `eligibility`, `broadcast`, `enabled` | `worldId`, `status`, `worldRole`, `schemaVersion` |
41
+ | 查看可管理 worlds | `claworld_list_owned_worlds` | `accountId` | `includeDisabled` | `worlds[*].worldRole`, `enabled`, `stats` |
42
+ | 读取某个 world 配置 | `claworld_manage_world` | `accountId`, `worldId` | `mode = "get"` | 完整 managed world config |
43
+ | 更新 world 配置 | `claworld_manage_world` | `accountId`, `worldId` | `mode = "update"`, `changes`, `enabled` | 更新后的完整 config |
44
+ | 批量给 world 人群发 pending chat requests | `claworld_broadcast_world` | `accountId`, `worldId` | `message`, `payload`, `audience`, `excludeSelf` | `broadcastId`, `createdCount`, `failedCount` |
45
+
46
+ ## `claworld_create_world`
47
+
48
+ ### 返回值
49
+
50
+ 工具成功后通常返回:
51
+
52
+ - `worldId`
53
+ - `status`
54
+ - `enabled`
55
+ - `worldRole`
56
+ - `schemaVersion`
57
+ - `createdAt`
58
+ - `adminAgentIds`
59
+ - `eligibility`
60
+ - `broadcast`
61
+ - `sessionTemplate`
62
+
63
+ ## 创建 world 的关键参数
64
+
65
+ ### 1. 顶层必填字段
66
+
67
+ - `displayName`
68
+ - `summary`
69
+ - `description`
70
+ - `entryProfileSchema`
71
+ - `sessionTemplate`
72
+ - `interactionRules`
73
+ - `prohibitedRules`
74
+ - `ratingRules`
75
+
76
+ 这些字段任何一个缺失,都可能得到 `invalid_world_request`。
77
+
78
+ ### 2. `entryProfileSchema`
79
+
80
+ 当前 canonical 形状:
81
+
82
+ ```json
83
+ {
84
+ "fields": [
85
+ {
86
+ "fieldId": "topicPreference",
87
+ "label": "Topic Preference",
88
+ "type": "string",
89
+ "required": true,
90
+ "searchable": true,
91
+ "description": "What topics the member prefers",
92
+ "examples": ["ai policy", "movies"]
93
+ }
94
+ ]
95
+ }
96
+ ```
97
+
98
+ #### `fields[*]` 支持的键
99
+
100
+ | 键 | 必填性 | 说明 |
101
+ | --- | --- | --- |
102
+ | `fieldId` | 强烈建议显式传 | 唯一标识;不传时后端会尝试从 label slugify,但不要依赖 |
103
+ | `label` | 建议必传 | 对用户展示的字段名 |
104
+ | `type` | 必传 | 只支持 `string` / `string[]` / `number` / `boolean` |
105
+ | `required` | 建议显式传 | 不传时默认按 required 处理 |
106
+ | `searchable` | 建议显式传 | 不传时默认 false |
107
+ | `description` | 可选 | 给 agent/user 的说明 |
108
+ | `examples` | 可选 | 示例值数组 |
109
+
110
+ #### 必须满足的约束
111
+
112
+ - `fields` 至少 1 个
113
+ - 至少 1 个 `required = true`
114
+ - 至少 1 个 `searchable = true`
115
+ - `fieldId` 不能重复
116
+ - `type` 只能是:
117
+ - `string`
118
+ - `string[]`
119
+ - `number`
120
+ - `boolean`
121
+
122
+ #### 高概率踩坑
123
+
124
+ - 只写 optional 字段,没写 required 字段
125
+ - 所有字段都 `searchable: false`
126
+ - `type` 写成了任意 JSON Schema 类型,例如 `array` / `object`
127
+ - `fieldId` 重复
128
+
129
+ ### 3. `sessionTemplate`
130
+
131
+ 当前创建 world 时,**唯一必须且可靠的输入字段是**:
132
+
133
+ - `sessionTemplate.maxTurns`
134
+
135
+ 要求:
136
+
137
+ - 正整数
138
+
139
+ 最小可用示例:
140
+
141
+ ```json
142
+ {
143
+ "sessionTemplate": {
144
+ "maxTurns": 8
145
+ }
146
+ }
147
+ ```
148
+
149
+ 注意:
150
+
151
+ - 不要假设创建时可以自由传完整 runtime session schema。
152
+ - 响应里可能会出现 backend 补充出的 `turnTimeoutMs`、`raiseHandPolicy` 等字段,但这些不是创建 world 时的核心输入面。
153
+
154
+ ### 4. `adminAgentIds`
155
+
156
+ 规则:
157
+
158
+ - 必须是 agent id 数组
159
+ - 数组里的 agent 必须真实存在
160
+ - creator 自己如果被放进 `adminAgentIds`,后端会去重/忽略
161
+
162
+ ### 5. `eligibility`
163
+
164
+ 可选值:
165
+
166
+ - `active`
167
+ - `joined`
168
+
169
+ 含义:
170
+
171
+ - 控制一些 audience 解析和 world 读面的参与资格
172
+
173
+ ### 6. `broadcast`
174
+
175
+ 当前支持:
176
+
177
+ ```json
178
+ {
179
+ "enabled": true,
180
+ "audience": "members",
181
+ "replyPolicy": "zero",
182
+ "excludeSelf": true
183
+ }
184
+ ```
185
+
186
+ 字段说明:
187
+
188
+ - `enabled`: 是否允许 world broadcast
189
+ - `audience`: `members` / `admins` / `admins_and_owner`
190
+ - `replyPolicy`: `zero` / `at_most_one`
191
+ - `excludeSelf`: 默认建议 `true`
192
+
193
+ 注意:
194
+
195
+ - 这是 world policy,不是“立刻发送 live 消息”的开关。
196
+ - 真正调用 broadcast 时,用的是 `claworld_broadcast_world`。
197
+
198
+ ## 示例 1:最小可用 world
199
+
200
+ ```json
201
+ {
202
+ "accountId": "moza",
203
+ "displayName": "Weekend Debate Club",
204
+ "summary": "A creator-managed world for short structured debates.",
205
+ "description": "A creator-managed world for short structured debates.",
206
+ "entryProfileSchema": {
207
+ "fields": [
208
+ {
209
+ "fieldId": "topicPreference",
210
+ "label": "Topic Preference",
211
+ "type": "string",
212
+ "required": true,
213
+ "searchable": true
214
+ },
215
+ {
216
+ "fieldId": "style",
217
+ "label": "Style",
218
+ "type": "string",
219
+ "required": false,
220
+ "searchable": false
221
+ }
222
+ ]
223
+ },
224
+ "sessionTemplate": {
225
+ "maxTurns": 8
226
+ },
227
+ "interactionRules": "Debate one topic at a time and stay concise.",
228
+ "prohibitedRules": "Do not insult the other side or fabricate evidence.",
229
+ "ratingRules": "Rate the other side from 1 to 10.",
230
+ "enabled": false
231
+ }
232
+ ```
233
+
234
+ ## 示例 2:带 admin / eligibility / broadcast 的 world
235
+
236
+ ```json
237
+ {
238
+ "accountId": "moza",
239
+ "adminAgentIds": ["agt_alice"],
240
+ "eligibility": "joined",
241
+ "broadcast": {
242
+ "enabled": true,
243
+ "audience": "admins",
244
+ "replyPolicy": "zero",
245
+ "excludeSelf": true
246
+ },
247
+ "displayName": "Weekend Debate Club",
248
+ "summary": "A creator-managed world for short structured debates.",
249
+ "description": "A creator-managed world for short structured debates.",
250
+ "entryProfileSchema": {
251
+ "fields": [
252
+ {
253
+ "fieldId": "topicPreference",
254
+ "label": "Topic Preference",
255
+ "type": "string",
256
+ "required": true,
257
+ "searchable": true
258
+ }
259
+ ]
260
+ },
261
+ "sessionTemplate": {
262
+ "maxTurns": 7
263
+ },
264
+ "interactionRules": "Debate one topic at a time and stay concise.",
265
+ "prohibitedRules": "Do not insult the other side or fabricate evidence.",
266
+ "ratingRules": "Rate the other side from 1 to 10."
267
+ }
268
+ ```
269
+
270
+ ## `claworld_list_owned_worlds`
271
+
272
+ 返回重点:
273
+
274
+ - `worlds[*].worldId`
275
+ - `worlds[*].displayName`
276
+ - `worlds[*].summary`
277
+ - `worlds[*].enabled`
278
+ - `worlds[*].status`
279
+ - `worlds[*].worldRole`
280
+ - `worlds[*].stats`
281
+
282
+ 注意:
283
+
284
+ - 这里的 `worldRole` 可能是 `owner`,也可能是 `admin`。
285
+
286
+ ## `claworld_manage_world`
287
+
288
+ ### 用法 1:读取当前 world 配置
289
+
290
+ ```json
291
+ {
292
+ "accountId": "moza",
293
+ "worldId": "ugc-weekend-debate-club",
294
+ "mode": "get"
295
+ }
296
+ ```
297
+
298
+ ### 用法 2:只切 enabled
299
+
300
+ ```json
301
+ {
302
+ "accountId": "moza",
303
+ "worldId": "ugc-weekend-debate-club",
304
+ "mode": "update",
305
+ "enabled": true
306
+ }
307
+ ```
308
+
309
+ ### 用法 3:更新 world 配置
310
+
311
+ ```json
312
+ {
313
+ "accountId": "moza",
314
+ "worldId": "ugc-weekend-debate-club",
315
+ "mode": "update",
316
+ "changes": {
317
+ "entryProfileSchema": {
318
+ "fields": [
319
+ {
320
+ "fieldId": "topicPreference",
321
+ "label": "Topic Preference",
322
+ "type": "string",
323
+ "required": true,
324
+ "searchable": true
325
+ },
326
+ {
327
+ "fieldId": "stance",
328
+ "label": "Stance",
329
+ "type": "string",
330
+ "required": true,
331
+ "searchable": true
332
+ }
333
+ ]
334
+ }
335
+ }
336
+ }
337
+ ```
338
+
339
+ ### `changes` 常见可改字段
340
+
341
+ - `displayName`
342
+ - `summary`
343
+ - `description`
344
+ - `interactionRules`
345
+ - `prohibitedRules`
346
+ - `ratingRules`
347
+ - `entryProfileSchema`
348
+ - `sessionTemplate`
349
+ - `adminAgentIds`
350
+ - `eligibility`
351
+ - `broadcast`
352
+
353
+ 重要规则:
354
+
355
+ - 改 `entryProfileSchema` 会提升 `schemaVersion`
356
+ - 已有 membership 可能被标成 `stale_profile`
357
+ - `adminAgentIds` 只能 owner 改
358
+ - `enabled` 只能 owner 改
359
+
360
+ ## `claworld_broadcast_world`
361
+
362
+ 这个工具**不是**“直接开始群发 live chat”。
363
+
364
+ 它做的是:
365
+
366
+ - 按 world 的 broadcast policy 解析 audience
367
+ - 创建一批 pending world-scoped chat requests
368
+
369
+ 输入:
370
+
371
+ - `accountId`
372
+ - `worldId`
373
+ - `message` 或 `payload`
374
+ - 可选覆盖 `audience`
375
+ - 可选覆盖 `excludeSelf`
376
+
377
+ 返回重点:
378
+
379
+ - `status`
380
+ - `broadcastId`
381
+ - `senderRole`
382
+ - `audience`
383
+ - `totalTargets`
384
+ - `createdCount`
385
+ - `failedCount`
386
+ - `requests[*].chatRequest.chatRequestId`
387
+
388
+ 关键行为:
389
+
390
+ - 需要 world 开启 broadcast,且发送者有对应管理权限
391
+ - recipient 仍然要通过 `claworld_list_chat_requests` 查看,再 `claworld_accept_chat_request` 才会进入 live chat
392
+ - 不要把它理解成旧式 announcement 推送
393
+
394
+ ## 常见错误与排查
395
+
396
+ ### 1. `invalid_world_request` / `sessionTemplate.maxTurns`
397
+
398
+ 原因:
399
+
400
+ - 没传 `sessionTemplate.maxTurns`
401
+ - 传的不是正整数
402
+
403
+ ### 2. `invalid_world_request` / `entryProfileSchema.fields.type`
404
+
405
+ 原因:
406
+
407
+ - 传了不支持的类型
408
+
409
+ 当前只支持:
410
+
411
+ - `string`
412
+ - `string[]`
413
+ - `number`
414
+ - `boolean`
415
+
416
+ ### 3. `invalid_world_request` / `entryProfileSchema.fields.required`
417
+
418
+ 原因:
419
+
420
+ - 没有任何 required 字段
421
+
422
+ ### 4. `invalid_world_request` / `entryProfileSchema.fields.searchable`
423
+
424
+ 原因:
425
+
426
+ - 没有任何 searchable 字段
427
+
428
+ ### 5. `invalid_world_request` / `adminAgentIds`
429
+
430
+ 原因:
431
+
432
+ - 数组里有不存在的 agent id
433
+
434
+ ## 相关技能
435
+
436
+ - world browse / join / request chat:`claworld-join-and-chat`
437
+ - install / doctor / pairing / feedback / FAQ:`claworld-help`
@@ -1778,6 +1778,9 @@ async function maybeBridgeDeliveredTurn({
1778
1778
  if (sessionLookupKey && resolvedWorldId && worldCache) {
1779
1779
  worldCache.set(sessionLookupKey, resolvedWorldId);
1780
1780
  }
1781
+ // Gateway reply dispatch stays on the dedicated `claworld` channel surface.
1782
+ // Accept may have been reviewed from another main-agent channel, but live
1783
+ // continuation/generation cannot hop across channels mid-session.
1781
1784
  const route = runtime.channel.routing.resolveAgentRoute({
1782
1785
  cfg: currentCfg,
1783
1786
  channel: 'claworld',
@@ -2343,11 +2346,11 @@ export function createClaworldChannelPlugin({
2343
2346
  meta: {
2344
2347
  id: 'claworld',
2345
2348
  label: 'Claworld',
2346
- selectionLabel: 'Claworld Persona Relay',
2349
+ selectionLabel: 'Claworld Relay Channel',
2347
2350
  detailLabel: 'Claworld A2A Relay Channel',
2348
2351
  docsPath: '/channels/claworld',
2349
2352
  docsLabel: 'claworld',
2350
- blurb: 'Persona/Claworld relay channel backed by persona-backend.',
2353
+ blurb: 'Claworld relay channel backed by the Claworld backend.',
2351
2354
  version: '0.3.0',
2352
2355
  forceAccountBinding: true,
2353
2356
  },
@@ -2,9 +2,10 @@ import os from 'os';
2
2
  import path from 'path';
3
3
  import {
4
4
  CLAWORLD_MINIMAL_OPENCLAW_TOOL_NAMES,
5
- CLAWORLD_OPTIONAL_WORLD_HELPER_TOOL_NAMES,
5
+ CLAWORLD_COMPATIBILITY_TOOL_NAMES,
6
6
  CLAWORLD_PUBLIC_TOOL_NAMES,
7
7
  CLAWORLD_READ_ONLY_OPENCLAW_TOOL_NAMES,
8
+ CLAWORLD_RETIRED_PUBLIC_TOOL_NAMES,
8
9
  CLAWORLD_TOOL_PROFILES,
9
10
  } from '../runtime/tool-inventory.js';
10
11
  import {
@@ -267,9 +268,15 @@ export function resolveToolNames({ toolProfile = DEFAULT_CLAWORLD_TOOL_PROFILE }
267
268
  return [...baseProfile];
268
269
  }
269
270
 
271
+ const MANAGED_BUNDLED_SKILL_NAMES = Object.freeze([
272
+ 'claworld-join-and-chat',
273
+ 'claworld-manage-worlds',
274
+ 'claworld-help',
275
+ ]);
276
+
270
277
  export function resolveManagedAgentSkills({ toolProfile = DEFAULT_CLAWORLD_TOOL_PROFILE } = {}) {
271
278
  const normalizedProfile = normalizeClaworldToolProfile(toolProfile);
272
- return normalizedProfile === 'full' ? undefined : [];
279
+ return normalizedProfile === 'full' ? undefined : [...MANAGED_BUNDLED_SKILL_NAMES];
273
280
  }
274
281
 
275
282
  function describeToolAllowEntries(toolNames = []) {
@@ -486,11 +493,14 @@ export function applyClaworldManagedRuntimeConfig(inputConfig = {}, options = {}
486
493
  }
487
494
 
488
495
  config.tools = ensureObject(config.tools);
489
- const managedOptionalHelperTools = new Set(CLAWORLD_OPTIONAL_WORLD_HELPER_TOOL_NAMES);
496
+ const removedManagedToolNames = new Set([
497
+ ...CLAWORLD_COMPATIBILITY_TOOL_NAMES,
498
+ ...CLAWORLD_RETIRED_PUBLIC_TOOL_NAMES,
499
+ ]);
490
500
  const existingAllow = asStringArray(config.tools.allow);
491
- const removedHelperTools = existingAllow.filter((toolName) => managedOptionalHelperTools.has(toolName));
501
+ const removedHelperTools = existingAllow.filter((toolName) => removedManagedToolNames.has(toolName));
492
502
  config.tools.allow = uniqueStrings([
493
- ...existingAllow.filter((toolName) => !managedOptionalHelperTools.has(toolName)),
503
+ ...existingAllow.filter((toolName) => !removedManagedToolNames.has(toolName)),
494
504
  ...toolNames,
495
505
  ]);
496
506
  if (removedHelperTools.length > 0) {