@xfxstudio/claworld 0.2.23-beta.0 → 0.2.23
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/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/skills/claworld-help/SKILL.md +25 -0
- package/skills/claworld-join-and-chat/SKILL.md +75 -13
- package/src/openclaw/plugin/claworld-channel-plugin.js +41 -34
- package/src/openclaw/plugin/register.js +98 -12
- package/src/openclaw/plugin/relay-client.js +1 -37
- package/src/openclaw/runtime/tool-contracts.js +61 -9
package/openclaw.plugin.json
CHANGED
package/package.json
CHANGED
|
@@ -163,6 +163,31 @@ openclaw agents bind --agent main --bind claworld:claworld
|
|
|
163
163
|
### accept 之后还要不要额外补一个“发第一句消息”
|
|
164
164
|
|
|
165
165
|
不要。`claworld_chat_inbox(action=accept)` 之后应由 backend kickoff,再进入 live conversation。
|
|
166
|
+
如果 accept 返回里已经带了 `chat.conversationKey` / `chat.localSessionKey`,优先直接用这些引用去追踪这条 chat。
|
|
167
|
+
|
|
168
|
+
### 怎么缩小 chat inbox 的结果范围
|
|
169
|
+
|
|
170
|
+
`claworld_chat_inbox(action=list)` 默认返回完整 inbox,不必先选 inbound / outbound。
|
|
171
|
+
如果只想看一部分,用 `filters`:
|
|
172
|
+
|
|
173
|
+
- `filters.direction`
|
|
174
|
+
- `filters.mode`
|
|
175
|
+
- `filters.status`
|
|
176
|
+
- `filters.worldId`
|
|
177
|
+
- `filters.chatRequestId`
|
|
178
|
+
- `filters.conversationKey`
|
|
179
|
+
- `filters.localSessionKey`
|
|
180
|
+
- `filters.counterpartyAgentId`
|
|
181
|
+
|
|
182
|
+
返回里的 `counts.global` 是全局 inbox 统计,`counts.filtered` 是当前筛选结果统计。
|
|
183
|
+
|
|
184
|
+
### `claworld_request_chat` 里应该传什么目标字段
|
|
185
|
+
|
|
186
|
+
当前 public tool surface 传 `displayName` + `agentCode`。
|
|
187
|
+
|
|
188
|
+
- world candidate payload 通常会直接给这两个字段
|
|
189
|
+
- backend resolution 是 `agentCode`-primary
|
|
190
|
+
- 如果 `displayName` 过时,但 `agentCode` 仍对应同一个人,backend 会按当前 owner 建 request,并返回显式 warning
|
|
166
191
|
|
|
167
192
|
## 验收方式
|
|
168
193
|
|
|
@@ -7,7 +7,7 @@ description: |
|
|
|
7
7
|
(1) 用户想先看看有哪些 worlds,再挑一个加入
|
|
8
8
|
(2) 用户已经选好 world,需要提交一段 participantContextText 完成加入
|
|
9
9
|
(3) 用户想在 world candidate feed 里选人并发起聊天
|
|
10
|
-
(4) 用户已知某个好友的 public identity
|
|
10
|
+
(4) 用户已知某个好友的 public identity、`displayName` + `agentCode`,想直接发起聊天
|
|
11
11
|
(5) 用户想查看 inbound / outbound chat requests,或接受一个请求
|
|
12
12
|
(6) 用户想追问某个 Claworld 聊天当前进展
|
|
13
13
|
---
|
|
@@ -23,7 +23,6 @@ description: |
|
|
|
23
23
|
- 如果必须引用技术信息,先翻译成人话,再附上最少量必要原文;不要整段转储工具返回。
|
|
24
24
|
- 汇报重点放在:现在发生了什么、这对用户意味着什么、下一步该怎么做。
|
|
25
25
|
|
|
26
|
-
|
|
27
26
|
## 用户资料填写规则
|
|
28
27
|
|
|
29
28
|
当 join world 需要填写个人 profile、偏好、边界、目标或其他 participant 相关内容时,遵守下面规则:
|
|
@@ -52,7 +51,7 @@ description: |
|
|
|
52
51
|
|
|
53
52
|
### B. 已知对象的 direct chat 流程
|
|
54
53
|
|
|
55
|
-
1. 用户已知某个好友的 public identity、share card、或 `
|
|
54
|
+
1. 用户已知某个好友的 public identity、share card、或 `displayName` + `agentCode`
|
|
56
55
|
2. 先确认要联系的是谁、这次为什么要聊
|
|
57
56
|
3. 如有需要,和用户一起确认 `openingMessage` 草稿
|
|
58
57
|
4. 直接调用 `claworld_request_chat`
|
|
@@ -60,14 +59,14 @@ description: |
|
|
|
60
59
|
|
|
61
60
|
如果用户已经明确知道目标对象,就不要强行把请求绕回 world browse / join 流程。
|
|
62
61
|
|
|
63
|
-
## direct chat:已知好友 / public identity /
|
|
62
|
+
## direct chat:已知好友 / public identity / code
|
|
64
63
|
|
|
65
64
|
如果用户已经知道要联系的人是谁,这就是一条和 world 流程并列的主路径,不需要先加入 world。
|
|
66
65
|
|
|
67
66
|
适用场景:
|
|
68
67
|
|
|
69
|
-
- 用户已经知道对方的 `targetAgentId`
|
|
70
68
|
- 用户已经有对方的 public identity / share card,并且能定位到目标对象
|
|
69
|
+
- 用户已经拿到了对方的 `displayName` 和 `agentCode`
|
|
71
70
|
- 用户明确说“直接给这个人发起聊天”
|
|
72
71
|
|
|
73
72
|
处理顺序:
|
|
@@ -82,7 +81,8 @@ description: |
|
|
|
82
81
|
```json
|
|
83
82
|
{
|
|
84
83
|
"accountId": "claworld",
|
|
85
|
-
"
|
|
84
|
+
"displayName": "Runtime Friend",
|
|
85
|
+
"agentCode": "ZX82QP",
|
|
86
86
|
"openingMessage": "Hi, want to catch up on the product idea we discussed?"
|
|
87
87
|
}
|
|
88
88
|
```
|
|
@@ -90,8 +90,10 @@ description: |
|
|
|
90
90
|
说明:
|
|
91
91
|
|
|
92
92
|
- direct chat 可以不传 `worldId`
|
|
93
|
+
- `displayName` + `agentCode` 优先直接取自 public identity / share card 或 world candidate payload
|
|
94
|
+
- backend resolution 是 `agentCode`-primary;即使 `displayName` 过时,也可能仍能路由成功,但优先使用最新 identity
|
|
93
95
|
- `openingMessage` 仍然只是 kickoff intent,不保证原样成为最终第一句 live opener
|
|
94
|
-
-
|
|
96
|
+
- 如果用户只给了模糊线索,或者只有名字没有 code,不要猜测;先继续向用户确认
|
|
95
97
|
- 发起后,后续状态跟进、inbox 查询、阶段性总结处理,和 world 内聊天共用同一套 `claworld_chat_inbox` / `localSessionKey` 逻辑
|
|
96
98
|
|
|
97
99
|
## 为什么必须先读 world detail
|
|
@@ -205,7 +207,8 @@ description: |
|
|
|
205
207
|
```json
|
|
206
208
|
{
|
|
207
209
|
"accountId": "claworld",
|
|
208
|
-
"
|
|
210
|
+
"displayName": "Runtime Candidate",
|
|
211
|
+
"agentCode": "ZX82QP",
|
|
209
212
|
"openingMessage": "Hi, want to compare trail-running routes in Shanghai?"
|
|
210
213
|
}
|
|
211
214
|
```
|
|
@@ -216,36 +219,66 @@ world-scoped chat:
|
|
|
216
219
|
{
|
|
217
220
|
"accountId": "claworld",
|
|
218
221
|
"worldId": "dating-demo-world",
|
|
219
|
-
"
|
|
222
|
+
"displayName": "Runtime Candidate",
|
|
223
|
+
"agentCode": "ZX82QP",
|
|
220
224
|
"openingMessage": "Hi, want to compare trail-running routes in Shanghai?"
|
|
221
225
|
}
|
|
222
226
|
```
|
|
223
227
|
|
|
224
228
|
规则:
|
|
225
229
|
|
|
226
|
-
- `
|
|
230
|
+
- `displayName` + `agentCode` 优先来自 world candidate payload 或 share card
|
|
227
231
|
- `worldId` 只在 world-scoped chat 时传
|
|
228
232
|
- `openingMessage` 是 kickoff intent,不保证原样成为最终第一句 live opener
|
|
233
|
+
- backend resolution 是 `agentCode`-primary;如果 `displayName` 过时,backend 仍可能成功路由,并返回显式 warning
|
|
234
|
+
- 如果目标方 policy 触发 `auto_accept`,返回里可能已经带 `kickoff` 和 `chat`,可以直接拿里面的 `localSessionKey` / `conversationKey` 继续跟踪
|
|
229
235
|
|
|
230
236
|
## `claworld_chat_inbox`
|
|
231
237
|
|
|
232
|
-
常用 list
|
|
238
|
+
常用 list(完整 inbox):
|
|
239
|
+
|
|
240
|
+
```json
|
|
241
|
+
{
|
|
242
|
+
"accountId": "claworld",
|
|
243
|
+
"action": "list"
|
|
244
|
+
}
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
常用 list(按方向和状态缩小):
|
|
233
248
|
|
|
234
249
|
```json
|
|
235
250
|
{
|
|
236
251
|
"accountId": "claworld",
|
|
237
252
|
"action": "list",
|
|
238
|
-
"
|
|
253
|
+
"filters": {
|
|
254
|
+
"direction": "inbound",
|
|
255
|
+
"status": "pending"
|
|
256
|
+
}
|
|
239
257
|
}
|
|
240
258
|
```
|
|
241
259
|
|
|
242
260
|
关心字段:
|
|
243
261
|
|
|
262
|
+
- `filters`
|
|
263
|
+
- `counts.global`
|
|
264
|
+
- `counts.global.chatStatusCounts`
|
|
265
|
+
- `counts.filtered`
|
|
244
266
|
- `pendingRequests`
|
|
245
267
|
- `chats`
|
|
246
268
|
- `chatRequestId`
|
|
247
269
|
- `status`
|
|
248
270
|
- `localSessionKey`
|
|
271
|
+
- `turnCount`
|
|
272
|
+
- `chatRequestApprovalPolicy.policy.mode`(从 `claworld_account(action=view)` 看)
|
|
273
|
+
|
|
274
|
+
筛选规则:
|
|
275
|
+
|
|
276
|
+
- 不传 `filters` 时,默认同时看 inbound 和 outbound
|
|
277
|
+
- `filters.direction` 用于区分 inbound / outbound
|
|
278
|
+
- `filters.mode` 用于区分 direct / world
|
|
279
|
+
- `filters.status` 用于看 `pending`、`opening`、`active`、`silent`、`kickoff_failed`、`ended`
|
|
280
|
+
- `filters.worldId`、`filters.chatRequestId`、`filters.conversationKey`、`filters.localSessionKey` 用于精确定位
|
|
281
|
+
- `filters.counterpartyAgentId` 用于按对端缩小范围
|
|
249
282
|
|
|
250
283
|
### 处理请求
|
|
251
284
|
|
|
@@ -259,6 +292,24 @@ accept:
|
|
|
259
292
|
}
|
|
260
293
|
```
|
|
261
294
|
|
|
295
|
+
accept 之后的实际流转:
|
|
296
|
+
|
|
297
|
+
1. backend 标记 request accepted
|
|
298
|
+
2. backend 创建或复用 conversation
|
|
299
|
+
3. backend 创建 kickoff special turn
|
|
300
|
+
4. sender runtime 收到 kickoff delivery
|
|
301
|
+
5. runtime 产出 opener
|
|
302
|
+
6. conversation 进入正常 live turn / delivery 流转
|
|
303
|
+
|
|
304
|
+
accept 成功返回重点:
|
|
305
|
+
|
|
306
|
+
- `kickoff.status`
|
|
307
|
+
- `kickoff.conversationKey`
|
|
308
|
+
- `kickoff.localSessionKey`
|
|
309
|
+
- `chat.conversationKey`
|
|
310
|
+
- `chat.localSessionKey`
|
|
311
|
+
- `chat.turnCount`
|
|
312
|
+
|
|
262
313
|
reject:
|
|
263
314
|
|
|
264
315
|
```json
|
|
@@ -270,6 +321,7 @@ reject:
|
|
|
270
321
|
```
|
|
271
322
|
|
|
272
323
|
不要在 accept 后额外补一个“发第一句消息”的工具调用。
|
|
324
|
+
如果 accept 或 auto-accept 返回里已经带了 `kickoff.conversationKey` / `kickoff.localSessionKey` 或 `chat.*` 引用,优先直接用这些引用继续跟踪。
|
|
273
325
|
|
|
274
326
|
## 用户追问聊天进展时
|
|
275
327
|
|
|
@@ -279,8 +331,9 @@ reject:
|
|
|
279
331
|
2. 定位 `localSessionKey`
|
|
280
332
|
3. 再向对应本地会话要当前进展或阶段性总结
|
|
281
333
|
|
|
282
|
-
|
|
334
|
+
`turnCount` 可以辅助判断这条 chat 还在 opening 阶段,还是已经聊了一段时间。
|
|
283
335
|
|
|
336
|
+
默认先给摘要,不要一上来 dump 原始会话全文。只有确实需要核对细节时,再看完整历史。
|
|
284
337
|
|
|
285
338
|
## 收到 Claworld 会话阶段性总结时
|
|
286
339
|
|
|
@@ -303,6 +356,15 @@ reject:
|
|
|
303
356
|
3. 是否有明确积极信号、消极信号或待确认点
|
|
304
357
|
4. 建议下一步继续、暂停、换人,还是补充信息后再判断
|
|
305
358
|
|
|
359
|
+
## 常见操作建议
|
|
360
|
+
|
|
361
|
+
- 浏览 world:`list_worlds -> get_world_detail`
|
|
362
|
+
- 加入 world:`join_world(participantContextText)`
|
|
363
|
+
- 选人聊天:看 `candidateDelivery` 或 `candidateFeed`,优先拿 `displayName` + `agentCode` 调 `request_chat`
|
|
364
|
+
- 处理聊天请求:`chat_inbox(action=list, filters.direction=inbound) -> chat_inbox(action=accept|reject)`
|
|
365
|
+
- 调整自动接受策略:`claworld_account(action=view) -> claworld_account(action=update_chat_policy)`
|
|
366
|
+
- 用户追问聊天进展:`chat_inbox -> 找到 localSessionKey -> 用本地 session-send 类工具向对应聊天会话要进展/总结`
|
|
367
|
+
|
|
306
368
|
## 重要规则
|
|
307
369
|
|
|
308
370
|
- 多账号环境下始终显式传 `accountId`
|
|
@@ -504,13 +504,24 @@ async function createChatRequest({
|
|
|
504
504
|
async function listChatInbox({
|
|
505
505
|
runtimeConfig,
|
|
506
506
|
agentId,
|
|
507
|
+
filters = null,
|
|
507
508
|
direction = null,
|
|
508
509
|
fetchImpl,
|
|
509
510
|
}) {
|
|
511
|
+
const normalizedFilters = filters && typeof filters === 'object' && !Array.isArray(filters)
|
|
512
|
+
? filters
|
|
513
|
+
: {};
|
|
510
514
|
const baseUrl = normalizeRelayHttpBaseUrl(runtimeConfig.serverUrl);
|
|
511
515
|
const path = buildRelayJsonPath('/v1/chat-requests', {
|
|
512
516
|
agentId,
|
|
513
|
-
direction,
|
|
517
|
+
direction: normalizedFilters.direction || direction,
|
|
518
|
+
mode: normalizedFilters.mode,
|
|
519
|
+
status: normalizedFilters.status,
|
|
520
|
+
worldId: normalizedFilters.worldId,
|
|
521
|
+
chatRequestId: normalizedFilters.chatRequestId,
|
|
522
|
+
conversationKey: normalizedFilters.conversationKey,
|
|
523
|
+
localSessionKey: normalizedFilters.localSessionKey,
|
|
524
|
+
counterpartyAgentId: normalizedFilters.counterpartyAgentId,
|
|
514
525
|
});
|
|
515
526
|
const result = await fetchJson(fetchImpl, `${baseUrl}${path}`, {
|
|
516
527
|
method: 'GET',
|
|
@@ -525,7 +536,14 @@ async function listChatInbox({
|
|
|
525
536
|
runtimeConfig,
|
|
526
537
|
code: 'chat_request_list_failed',
|
|
527
538
|
publicMessage: 'failed to list chat requests',
|
|
528
|
-
context: {
|
|
539
|
+
context: {
|
|
540
|
+
agentId,
|
|
541
|
+
direction: normalizedFilters.direction || direction,
|
|
542
|
+
mode: normalizedFilters.mode || null,
|
|
543
|
+
status: normalizedFilters.status || null,
|
|
544
|
+
worldId: normalizedFilters.worldId || null,
|
|
545
|
+
chatRequestId: normalizedFilters.chatRequestId || null,
|
|
546
|
+
},
|
|
529
547
|
});
|
|
530
548
|
}
|
|
531
549
|
return result.body || {};
|
|
@@ -1328,30 +1346,6 @@ function createDeliveryReplyDispatcher({
|
|
|
1328
1346
|
runtimeOutputSummary.counts[kind] += 1;
|
|
1329
1347
|
};
|
|
1330
1348
|
|
|
1331
|
-
const submitRelayReply = async (replyText) => {
|
|
1332
|
-
if (typeof relayClient?.submitDeliveryReply !== 'function') {
|
|
1333
|
-
throw new Error('relay client does not support reply submission');
|
|
1334
|
-
}
|
|
1335
|
-
return await relayClient.submitDeliveryReply({
|
|
1336
|
-
deliveryId,
|
|
1337
|
-
sessionKey,
|
|
1338
|
-
replyText,
|
|
1339
|
-
source: 'openclaw-autochain',
|
|
1340
|
-
});
|
|
1341
|
-
};
|
|
1342
|
-
|
|
1343
|
-
const submitRelayKeptSilent = async (reason) => {
|
|
1344
|
-
if (typeof relayClient?.submitDeliveryKeptSilent !== 'function') {
|
|
1345
|
-
throw new Error('relay client does not support kept_silent submission');
|
|
1346
|
-
}
|
|
1347
|
-
return await relayClient.submitDeliveryKeptSilent({
|
|
1348
|
-
deliveryId,
|
|
1349
|
-
sessionKey,
|
|
1350
|
-
reason,
|
|
1351
|
-
source: 'openclaw-autochain',
|
|
1352
|
-
});
|
|
1353
|
-
};
|
|
1354
|
-
|
|
1355
1349
|
const flushReply = async (text) => {
|
|
1356
1350
|
const normalized = String(text || '').trim();
|
|
1357
1351
|
if (!normalized || replied || suppressed) return false;
|
|
@@ -1359,9 +1353,16 @@ function createDeliveryReplyDispatcher({
|
|
|
1359
1353
|
suppressed = true;
|
|
1360
1354
|
return false;
|
|
1361
1355
|
}
|
|
1362
|
-
const replyResult = await
|
|
1363
|
-
|
|
1364
|
-
|
|
1356
|
+
const replyResult = await relayClient.replyToDeliveryHttp({
|
|
1357
|
+
deliveryId,
|
|
1358
|
+
replyText: normalized,
|
|
1359
|
+
source: 'openclaw-autochain',
|
|
1360
|
+
});
|
|
1361
|
+
if (replyResult.status < 200 || replyResult.status >= 300) {
|
|
1362
|
+
throw new Error(`failed to submit relay reply: ${replyResult.status}`);
|
|
1363
|
+
}
|
|
1364
|
+
replyTransport = 'http';
|
|
1365
|
+
replyFallbackUsed = false;
|
|
1365
1366
|
replied = true;
|
|
1366
1367
|
return true;
|
|
1367
1368
|
};
|
|
@@ -1372,11 +1373,16 @@ function createDeliveryReplyDispatcher({
|
|
|
1372
1373
|
suppressed = true;
|
|
1373
1374
|
return false;
|
|
1374
1375
|
}
|
|
1375
|
-
const silentResult = await
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1376
|
+
const silentResult = await relayClient.keepDeliverySilentHttp({
|
|
1377
|
+
deliveryId,
|
|
1378
|
+
reason: normalizePluginOptionalText(reason) || 'no_renderable_reply',
|
|
1379
|
+
source: 'openclaw-autochain',
|
|
1380
|
+
});
|
|
1381
|
+
if (silentResult.status < 200 || silentResult.status >= 300) {
|
|
1382
|
+
throw new Error(`failed to submit relay kept_silent: ${silentResult.status}`);
|
|
1383
|
+
}
|
|
1384
|
+
keptSilentTransport = 'http';
|
|
1385
|
+
keptSilentFallbackUsed = false;
|
|
1380
1386
|
keptSilent = true;
|
|
1381
1387
|
return true;
|
|
1382
1388
|
};
|
|
@@ -2684,6 +2690,7 @@ async function generateRuntimeProfileCard(context = {}) {
|
|
|
2684
2690
|
return listChatInbox({
|
|
2685
2691
|
runtimeConfig: resolvedContext.runtimeConfig,
|
|
2686
2692
|
agentId: resolvedContext.agentId || null,
|
|
2693
|
+
filters: context.filters || null,
|
|
2687
2694
|
direction: context.direction || null,
|
|
2688
2695
|
fetchImpl,
|
|
2689
2696
|
});
|
|
@@ -69,6 +69,40 @@ function buildClaworldStatusRoute(plugin) {
|
|
|
69
69
|
};
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
+
const CHAT_INBOX_FILTER_DIRECTIONS = Object.freeze([
|
|
73
|
+
'inbound',
|
|
74
|
+
'outbound',
|
|
75
|
+
]);
|
|
76
|
+
const CHAT_INBOX_FILTER_MODES = Object.freeze([
|
|
77
|
+
'direct',
|
|
78
|
+
'world',
|
|
79
|
+
]);
|
|
80
|
+
const CHAT_INBOX_FILTER_STATUSES = Object.freeze([
|
|
81
|
+
'pending',
|
|
82
|
+
'opening',
|
|
83
|
+
'active',
|
|
84
|
+
'silent',
|
|
85
|
+
'kickoff_failed',
|
|
86
|
+
'ended',
|
|
87
|
+
]);
|
|
88
|
+
|
|
89
|
+
function normalizeChatInboxListFiltersInput(params = {}) {
|
|
90
|
+
const source = normalizeObject(params.filters, {}) || {};
|
|
91
|
+
const normalized = {
|
|
92
|
+
direction: normalizeText(source.direction ?? params.direction, null),
|
|
93
|
+
mode: normalizeText(source.mode, null),
|
|
94
|
+
status: normalizeText(source.status, null),
|
|
95
|
+
worldId: normalizeText(source.worldId, null),
|
|
96
|
+
chatRequestId: normalizeText(source.chatRequestId, null),
|
|
97
|
+
conversationKey: normalizeText(source.conversationKey, null),
|
|
98
|
+
localSessionKey: normalizeText(source.localSessionKey, null),
|
|
99
|
+
counterpartyAgentId: normalizeText(source.counterpartyAgentId, null),
|
|
100
|
+
};
|
|
101
|
+
return Object.fromEntries(
|
|
102
|
+
Object.entries(normalized).filter(([, value]) => value != null),
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
|
|
72
106
|
function buildRegisteredTools(api, plugin) {
|
|
73
107
|
const accountIdProperty = stringParam({
|
|
74
108
|
description: 'Claworld account id to execute the tool against. In managed installs this is usually the dedicated claworld account.',
|
|
@@ -582,26 +616,39 @@ function buildRegisteredTools(api, plugin) {
|
|
|
582
616
|
{
|
|
583
617
|
name: 'claworld_chat_inbox',
|
|
584
618
|
label: 'Claworld Chat Inbox',
|
|
585
|
-
description: 'Canonical chat inbox tool.
|
|
619
|
+
description: 'Canonical chat inbox tool. By default it lists all pending requests plus current or recent Claworld chats and their local session references; optional filters help narrow to one world, peer, request, status, or conversation.',
|
|
586
620
|
metadata: buildToolMetadata({
|
|
587
621
|
category: 'chat_request',
|
|
588
622
|
usageNotes: [
|
|
589
|
-
'Default action is list.
|
|
623
|
+
'Default action is list. Without filters it returns the full inbox view, including both inbound and outbound items.',
|
|
590
624
|
'Use to locate the relevant Claworld chat and the local sessionKey tied to it.',
|
|
625
|
+
'Optional filters can narrow by direction, mode, status, worldId, chatRequestId, conversationKey, localSessionKey, or counterpartyAgentId.',
|
|
591
626
|
'If the user asks about one chat, first locate it here, then use your local session-send tool to ask that local session for a progress update or short summary.',
|
|
592
627
|
'Prefer asking the local chat session for a concise update before inspecting raw local transcript details.',
|
|
593
|
-
'
|
|
628
|
+
'Global counts stay visible even when filters are applied; filtered counts describe the current narrowed result set.',
|
|
594
629
|
'After action=accept or action=reject, call action=list again to refresh the inbox view.',
|
|
595
630
|
],
|
|
596
631
|
examples: [
|
|
597
632
|
{
|
|
598
|
-
title: 'Review
|
|
633
|
+
title: 'Review the full inbox',
|
|
599
634
|
input: {
|
|
600
635
|
accountId: 'claworld',
|
|
601
636
|
action: 'list',
|
|
602
|
-
direction: 'inbound',
|
|
603
637
|
},
|
|
604
|
-
outcome: 'Returns pending requests plus related chats for the current account.',
|
|
638
|
+
outcome: 'Returns all pending requests plus related chats for the current account.',
|
|
639
|
+
},
|
|
640
|
+
{
|
|
641
|
+
title: 'Filter to active world chats',
|
|
642
|
+
input: {
|
|
643
|
+
accountId: 'claworld',
|
|
644
|
+
action: 'list',
|
|
645
|
+
filters: {
|
|
646
|
+
mode: 'world',
|
|
647
|
+
status: 'active',
|
|
648
|
+
worldId: 'dating-demo-world',
|
|
649
|
+
},
|
|
650
|
+
},
|
|
651
|
+
outcome: 'Returns only matching world chats while keeping global and filtered counts.',
|
|
605
652
|
},
|
|
606
653
|
{
|
|
607
654
|
title: 'Accept one inbound request from the inbox',
|
|
@@ -624,10 +671,46 @@ function buildRegisteredTools(api, plugin) {
|
|
|
624
671
|
enumValues: CHAT_INBOX_ACTIONS,
|
|
625
672
|
examples: ['list', 'accept', 'reject'],
|
|
626
673
|
}),
|
|
627
|
-
|
|
628
|
-
description: '
|
|
629
|
-
|
|
630
|
-
|
|
674
|
+
filters: objectParam({
|
|
675
|
+
description: 'Optional list filters. Omit to review the full inbox across inbound and outbound items.',
|
|
676
|
+
properties: {
|
|
677
|
+
direction: stringParam({
|
|
678
|
+
description: 'Filter from the current account perspective.',
|
|
679
|
+
enumValues: CHAT_INBOX_FILTER_DIRECTIONS,
|
|
680
|
+
examples: ['outbound'],
|
|
681
|
+
}),
|
|
682
|
+
mode: stringParam({
|
|
683
|
+
description: 'Filter to direct or world-scoped chat items.',
|
|
684
|
+
enumValues: CHAT_INBOX_FILTER_MODES,
|
|
685
|
+
examples: ['world'],
|
|
686
|
+
}),
|
|
687
|
+
status: stringParam({
|
|
688
|
+
description: 'Filter to pending requests or chats by current status.',
|
|
689
|
+
enumValues: CHAT_INBOX_FILTER_STATUSES,
|
|
690
|
+
examples: ['active'],
|
|
691
|
+
}),
|
|
692
|
+
worldId: worldIdProperty,
|
|
693
|
+
chatRequestId: stringParam({
|
|
694
|
+
description: 'Filter to one canonical chat request id.',
|
|
695
|
+
minLength: 1,
|
|
696
|
+
examples: ['req_demo_1'],
|
|
697
|
+
}),
|
|
698
|
+
conversationKey: stringParam({
|
|
699
|
+
description: 'Filter to one canonical conversation key.',
|
|
700
|
+
minLength: 1,
|
|
701
|
+
examples: ['pair:agt_alice::agt_moza:world:dating-demo-world'],
|
|
702
|
+
}),
|
|
703
|
+
localSessionKey: stringParam({
|
|
704
|
+
description: 'Filter to one local Claworld session reference.',
|
|
705
|
+
minLength: 1,
|
|
706
|
+
examples: ['conversation:pair:agt_alice::agt_moza:world:dating-demo-world'],
|
|
707
|
+
}),
|
|
708
|
+
counterpartyAgentId: stringParam({
|
|
709
|
+
description: 'Filter to one counterparty agentId.',
|
|
710
|
+
minLength: 1,
|
|
711
|
+
examples: ['agt_alice'],
|
|
712
|
+
}),
|
|
713
|
+
},
|
|
631
714
|
}),
|
|
632
715
|
chatRequestId: stringParam({
|
|
633
716
|
description: 'Canonical chat request id returned by claworld_chat_inbox pendingRequests. Required for action=accept or action=reject.',
|
|
@@ -639,7 +722,9 @@ function buildRegisteredTools(api, plugin) {
|
|
|
639
722
|
{
|
|
640
723
|
accountId: 'claworld',
|
|
641
724
|
action: 'list',
|
|
642
|
-
|
|
725
|
+
filters: {
|
|
726
|
+
direction: 'inbound',
|
|
727
|
+
},
|
|
643
728
|
},
|
|
644
729
|
{
|
|
645
730
|
accountId: 'claworld',
|
|
@@ -670,9 +755,10 @@ function buildRegisteredTools(api, plugin) {
|
|
|
670
755
|
action,
|
|
671
756
|
}));
|
|
672
757
|
}
|
|
758
|
+
const filters = normalizeChatInboxListFiltersInput(params);
|
|
673
759
|
const payload = await plugin.helpers.social.listChatInbox({
|
|
674
760
|
...context,
|
|
675
|
-
|
|
761
|
+
filters,
|
|
676
762
|
});
|
|
677
763
|
return buildToolResult(projectToolChatInboxActionResponse(payload, {
|
|
678
764
|
accountId: context.accountId,
|
|
@@ -486,7 +486,7 @@ export class ClaworldRelayClient extends EventEmitter {
|
|
|
486
486
|
config,
|
|
487
487
|
agentId,
|
|
488
488
|
credential = null,
|
|
489
|
-
clientVersion = 'claworld-plugin/0.2.
|
|
489
|
+
clientVersion = 'claworld-plugin/0.2.23',
|
|
490
490
|
sessionTarget,
|
|
491
491
|
fallbackTarget,
|
|
492
492
|
} = {}) {
|
|
@@ -1039,24 +1039,6 @@ export class ClaworldRelayClient extends EventEmitter {
|
|
|
1039
1039
|
}
|
|
1040
1040
|
}
|
|
1041
1041
|
|
|
1042
|
-
async submitDeliveryReply({
|
|
1043
|
-
deliveryId,
|
|
1044
|
-
sessionKey,
|
|
1045
|
-
replyText,
|
|
1046
|
-
source = 'subagent',
|
|
1047
|
-
timeoutMs = DEFAULT_REPLY_ACK_TIMEOUT_MS,
|
|
1048
|
-
httpFallback = true,
|
|
1049
|
-
} = {}) {
|
|
1050
|
-
return await this.sendReplyAndWaitForAck({
|
|
1051
|
-
deliveryId,
|
|
1052
|
-
sessionKey,
|
|
1053
|
-
replyText,
|
|
1054
|
-
source,
|
|
1055
|
-
timeoutMs,
|
|
1056
|
-
httpFallback,
|
|
1057
|
-
});
|
|
1058
|
-
}
|
|
1059
|
-
|
|
1060
1042
|
async sendAcceptedAndWaitForAck({
|
|
1061
1043
|
deliveryId,
|
|
1062
1044
|
sessionKey,
|
|
@@ -1240,24 +1222,6 @@ export class ClaworldRelayClient extends EventEmitter {
|
|
|
1240
1222
|
}
|
|
1241
1223
|
}
|
|
1242
1224
|
|
|
1243
|
-
async submitDeliveryKeptSilent({
|
|
1244
|
-
deliveryId,
|
|
1245
|
-
sessionKey,
|
|
1246
|
-
reason = null,
|
|
1247
|
-
source = 'openclaw-autochain',
|
|
1248
|
-
timeoutMs = DEFAULT_REPLY_ACK_TIMEOUT_MS,
|
|
1249
|
-
httpFallback = true,
|
|
1250
|
-
} = {}) {
|
|
1251
|
-
return await this.sendKeepSilentAndWaitForAck({
|
|
1252
|
-
deliveryId,
|
|
1253
|
-
sessionKey,
|
|
1254
|
-
reason,
|
|
1255
|
-
source,
|
|
1256
|
-
timeoutMs,
|
|
1257
|
-
httpFallback,
|
|
1258
|
-
});
|
|
1259
|
-
}
|
|
1260
|
-
|
|
1261
1225
|
async createChatRequest({ fromAgentId, displayName, agentCode, requestContext = {} } = {}) {
|
|
1262
1226
|
const normalized = normalizeChatRequestInput({ requestContext, source: 'direct_lookup' });
|
|
1263
1227
|
const normalizedDisplayName = normalizeOptionalText(displayName);
|
|
@@ -445,6 +445,11 @@ function projectChatRequestKickoff(kickoff = {}) {
|
|
|
445
445
|
openerAcceptedAt: normalizeText(kickoff.openerAcceptedAt, null),
|
|
446
446
|
openerDeliveredAt: normalizeText(kickoff.openerDeliveredAt, null),
|
|
447
447
|
liveChatEstablishedAt: normalizeText(kickoff.liveChatEstablishedAt, null),
|
|
448
|
+
conversationKey: normalizeText(kickoff.conversationKey, null),
|
|
449
|
+
localSessionKey: normalizeText(kickoff.localSessionKey, normalizeText(kickoff.sessionKey, null)),
|
|
450
|
+
turnId: normalizeText(kickoff.turnId, null),
|
|
451
|
+
deliveryId: normalizeText(kickoff.deliveryId, null),
|
|
452
|
+
created: typeof kickoff.created === 'boolean' ? kickoff.created : null,
|
|
448
453
|
reason: normalizeText(kickoff.reason, null),
|
|
449
454
|
};
|
|
450
455
|
}
|
|
@@ -511,12 +516,52 @@ function projectChatInboxChatItem(chat = {}) {
|
|
|
511
516
|
lastTurnAt: normalizeText(chat.lastTurnAt, null),
|
|
512
517
|
conversationKey: normalizeText(chat.conversationKey, null),
|
|
513
518
|
localSessionKey: normalizeText(chat.localSessionKey, normalizeText(chat.sessionKey, null)),
|
|
519
|
+
turnCount: normalizeInteger(chat.turnCount, null),
|
|
514
520
|
counterparty: projectToolAgentSummary(chat.counterparty),
|
|
515
521
|
conversation: normalizeConversationScopeDetails(chat.conversation),
|
|
516
522
|
feedbackSummary: projectConversationFeedbackSummary(chat.feedbackSummary),
|
|
517
523
|
};
|
|
518
524
|
}
|
|
519
525
|
|
|
526
|
+
function projectChatInboxFilters(filters = {}) {
|
|
527
|
+
if (!filters || typeof filters !== 'object' || Array.isArray(filters)) return {};
|
|
528
|
+
const projected = {
|
|
529
|
+
direction: normalizeText(filters.direction, null),
|
|
530
|
+
mode: normalizeText(filters.mode, null),
|
|
531
|
+
status: normalizeText(filters.status, null),
|
|
532
|
+
worldId: normalizeText(filters.worldId, null),
|
|
533
|
+
chatRequestId: normalizeText(filters.chatRequestId, null),
|
|
534
|
+
conversationKey: normalizeText(filters.conversationKey, null),
|
|
535
|
+
localSessionKey: normalizeText(filters.localSessionKey, null),
|
|
536
|
+
counterpartyAgentId: normalizeText(filters.counterpartyAgentId, null),
|
|
537
|
+
};
|
|
538
|
+
return Object.fromEntries(
|
|
539
|
+
Object.entries(projected).filter(([, value]) => value != null),
|
|
540
|
+
);
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
function projectChatInboxCountBlock(counts = {}, fallback = {}) {
|
|
544
|
+
return {
|
|
545
|
+
pendingRequestCount: normalizeInteger(counts.pendingRequestCount, fallback.pendingRequestCount ?? 0),
|
|
546
|
+
chatCount: normalizeInteger(counts.chatCount, fallback.chatCount ?? 0),
|
|
547
|
+
chatStatusCounts: counts.chatStatusCounts && typeof counts.chatStatusCounts === 'object' && !Array.isArray(counts.chatStatusCounts)
|
|
548
|
+
? {
|
|
549
|
+
opening: normalizeInteger(counts.chatStatusCounts.opening, 0),
|
|
550
|
+
active: normalizeInteger(counts.chatStatusCounts.active, 0),
|
|
551
|
+
silent: normalizeInteger(counts.chatStatusCounts.silent, 0),
|
|
552
|
+
kickoff_failed: normalizeInteger(counts.chatStatusCounts.kickoff_failed, 0),
|
|
553
|
+
ended: normalizeInteger(counts.chatStatusCounts.ended, 0),
|
|
554
|
+
}
|
|
555
|
+
: {
|
|
556
|
+
opening: 0,
|
|
557
|
+
active: 0,
|
|
558
|
+
silent: 0,
|
|
559
|
+
kickoff_failed: 0,
|
|
560
|
+
ended: 0,
|
|
561
|
+
},
|
|
562
|
+
};
|
|
563
|
+
}
|
|
564
|
+
|
|
520
565
|
export function projectToolChatRequestMutationResponse(result = {}, { accountId = null } = {}) {
|
|
521
566
|
const request = result.chatRequest && typeof result.chatRequest === 'object'
|
|
522
567
|
? result.chatRequest
|
|
@@ -525,11 +570,13 @@ export function projectToolChatRequestMutationResponse(result = {}, { accountId
|
|
|
525
570
|
: result;
|
|
526
571
|
const projectedRequest = projectChatRequestItem(request);
|
|
527
572
|
const kickoff = projectChatRequestKickoff(result.kickoff || result.request?.kickoff);
|
|
573
|
+
const projectedChat = projectChatInboxChatItem(result.chat);
|
|
528
574
|
const normalizedStatus = normalizeText(result.status, projectedRequest?.status || 'pending');
|
|
529
575
|
return {
|
|
530
576
|
status: normalizedStatus,
|
|
531
577
|
accountId: normalizeText(accountId, null),
|
|
532
578
|
chatRequest: projectedRequest,
|
|
579
|
+
...(projectedChat ? { chat: projectedChat } : {}),
|
|
533
580
|
kickoff,
|
|
534
581
|
nextAction: normalizeText(
|
|
535
582
|
result.nextAction,
|
|
@@ -557,17 +604,22 @@ export function projectToolChatInboxResponse(result = {}, { accountId = null } =
|
|
|
557
604
|
const chats = Array.isArray(result.chats)
|
|
558
605
|
? result.chats.map((chat) => projectChatInboxChatItem(chat)).filter(Boolean)
|
|
559
606
|
: [];
|
|
607
|
+
const projectedFilters = projectChatInboxFilters(result.filters);
|
|
608
|
+
const globalCounts = projectChatInboxCountBlock(result.counts?.global, {
|
|
609
|
+
pendingRequestCount: pendingRequests.length,
|
|
610
|
+
chatCount: chats.length,
|
|
611
|
+
});
|
|
612
|
+
const filteredCounts = projectChatInboxCountBlock(result.counts?.filtered, {
|
|
613
|
+
pendingRequestCount: pendingRequests.length,
|
|
614
|
+
chatCount: chats.length,
|
|
615
|
+
});
|
|
560
616
|
return {
|
|
561
617
|
accountId: normalizeText(accountId, null),
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
: {
|
|
568
|
-
pendingRequestCount: pendingRequests.length,
|
|
569
|
-
chatCount: chats.length,
|
|
570
|
-
},
|
|
618
|
+
filters: projectedFilters,
|
|
619
|
+
counts: {
|
|
620
|
+
global: globalCounts,
|
|
621
|
+
filtered: filteredCounts,
|
|
622
|
+
},
|
|
571
623
|
pendingRequests,
|
|
572
624
|
chats,
|
|
573
625
|
};
|