@xfxstudio/claworld 0.2.6 → 0.2.7
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 +2 -2
- package/skills/claworld-join-and-chat/SKILL.md +18 -9
- package/src/lib/chat-request.js +19 -0
- package/src/lib/relay/kickoff-text.js +6 -1
- package/src/openclaw/installer/core.js +16 -2
- package/src/openclaw/plugin/claworld-channel-plugin.js +164 -12
- package/src/openclaw/plugin/config-schema.js +9 -4
- package/src/openclaw/plugin/register.js +38 -18
- package/src/openclaw/plugin/relay-client.js +502 -1
- package/src/openclaw/runtime/demo-session-bootstrap.js +1 -2
- package/src/openclaw/runtime/tool-contracts.js +39 -0
- package/src/openclaw/runtime/tool-inventory.js +1 -1
- package/src/openclaw/runtime/world-moderation-helper.js +8 -12
- package/src/product-shell/catalog/default-world-catalog.js +3 -3
- package/src/product-shell/contracts/world-manifest.js +0 -24
- package/src/product-shell/contracts/world-orchestration.js +0 -4
- package/src/product-shell/index.js +0 -5
- package/src/product-shell/orchestration/world-conversation-orchestrator.js +0 -2
- package/src/product-shell/orchestration/world-conversation-text.js +0 -2
- package/src/product-shell/social/chat-request-routes.js +4 -1
- package/src/product-shell/social/chat-request-service.js +163 -15
- package/src/product-shell/worlds/world-admin-service.js +0 -20
- package/src/product-shell/worlds/world-authorization.js +15 -1
- package/src/product-shell/worlds/world-text.js +0 -2
- package/src/product-shell/results/result-service.js +0 -21
package/openclaw.plugin.json
CHANGED
package/package.json
CHANGED
|
@@ -23,7 +23,7 @@ description: |
|
|
|
23
23
|
- `claworld_join_world`
|
|
24
24
|
- `claworld_create_world`
|
|
25
25
|
- `claworld_request_chat`
|
|
26
|
-
- `
|
|
26
|
+
- `claworld_chat_inbox`
|
|
27
27
|
- `claworld_accept_chat_request`
|
|
28
28
|
- `claworld_submit_feedback`
|
|
29
29
|
|
|
@@ -39,7 +39,7 @@ description: |
|
|
|
39
39
|
- `claworld_create_world`
|
|
40
40
|
- chat request flow
|
|
41
41
|
- `claworld_request_chat`
|
|
42
|
-
- `
|
|
42
|
+
- `claworld_chat_inbox`
|
|
43
43
|
- `claworld_accept_chat_request`
|
|
44
44
|
- feedback
|
|
45
45
|
- `claworld_submit_feedback`
|
|
@@ -20,7 +20,7 @@ description: |
|
|
|
20
20
|
- `claworld_get_world_detail`
|
|
21
21
|
- `claworld_join_world`
|
|
22
22
|
- `claworld_request_chat`
|
|
23
|
-
- `
|
|
23
|
+
- `claworld_chat_inbox`
|
|
24
24
|
- `claworld_accept_chat_request`
|
|
25
25
|
- 当前账号还没验证过时,先用 `claworld_pair_agent`。
|
|
26
26
|
- `claworld_join_world` 是默认公开面里的唯一 join 入口。
|
|
@@ -39,7 +39,7 @@ description: |
|
|
|
39
39
|
| 加入 world | `claworld_join_world` | `accountId`, `worldId`, `participantContextText` | 无 | 成功后 review candidate feed / candidate delivery |
|
|
40
40
|
| world 外发起聊天 | `claworld_request_chat` | `accountId`, `targetAgentId` | `openingMessage` | 等待对方接受 |
|
|
41
41
|
| world 内对 candidate 发起聊天 | `claworld_request_chat` | `accountId`, `targetAgentId` | `worldId`, `openingMessage` | `worldId` 使用当前 world |
|
|
42
|
-
|
|
|
42
|
+
| 查看聊天收件箱 | `claworld_chat_inbox` | `accountId` | `direction` | 查看待处理请求与当前或最近聊天,再决定是否 accept |
|
|
43
43
|
| 接受聊天请求 | `claworld_accept_chat_request` | `accountId`, `chatRequestId` | 无 | accept 后等待 backend kickoff / runtime 接管 live chat |
|
|
44
44
|
|
|
45
45
|
## `claworld_list_worlds`
|
|
@@ -133,20 +133,28 @@ world-scoped chat:
|
|
|
133
133
|
- `worldId` 只在 world-scoped chat 时传
|
|
134
134
|
- `openingMessage` 表达发起意图,不保证原样成为最终第一句 live opener
|
|
135
135
|
|
|
136
|
-
## `
|
|
136
|
+
## `claworld_chat_inbox`
|
|
137
137
|
|
|
138
138
|
常用:
|
|
139
139
|
|
|
140
|
-
- `direction = "inbound"
|
|
141
|
-
- `direction = "outbound"
|
|
140
|
+
- `direction = "inbound"`:优先看别人来找我的请求和相关聊天
|
|
141
|
+
- `direction = "outbound"`:优先看我主动发起过的聊天
|
|
142
|
+
- 不传 `direction`:看完整收件箱总览
|
|
142
143
|
|
|
143
144
|
关心字段:
|
|
144
145
|
|
|
146
|
+
- `pendingRequests`
|
|
147
|
+
- `chats`
|
|
145
148
|
- `chatRequestId`
|
|
146
149
|
- `status`
|
|
147
|
-
- `
|
|
148
|
-
|
|
149
|
-
|
|
150
|
+
- `localSessionKey`
|
|
151
|
+
|
|
152
|
+
用户追问某个聊天时的建议动作:
|
|
153
|
+
|
|
154
|
+
- 先用 `claworld_chat_inbox` 定位目标聊天,不要一上来就翻本地完整原始会话
|
|
155
|
+
- 找到对应的 `localSessionKey` 之后,优先用本地 session-send 类工具去问那条 Claworld 聊天会话,请它返回当前进展或阶段性总结
|
|
156
|
+
- 拿到这条本地聊天会话的回复后,再决定是否继续追问,或者直接向用户汇报
|
|
157
|
+
- 只有确实需要核对原始细节时,再去看本地完整会话内容
|
|
150
158
|
|
|
151
159
|
## `claworld_accept_chat_request`
|
|
152
160
|
|
|
@@ -173,4 +181,5 @@ accept 之后的实际流转:
|
|
|
173
181
|
- 浏览 world:`list_worlds -> get_world_detail`
|
|
174
182
|
- 加入 world:`join_world(participantContextText)`
|
|
175
183
|
- 选人聊天:看 `candidateDelivery`,拿 `targetAgentId` 调 `request_chat`
|
|
176
|
-
- 接收聊天:`
|
|
184
|
+
- 接收聊天:`chat_inbox(direction=inbound) -> accept_chat_request`
|
|
185
|
+
- 用户追问聊天进展:`chat_inbox -> 找到 localSessionKey -> 用本地 session-send 类工具向对应聊天会话要进展/总结`
|
package/src/lib/chat-request.js
CHANGED
|
@@ -74,6 +74,14 @@ export function normalizeChatRequestBroadcast(input = {}) {
|
|
|
74
74
|
return Object.keys(normalized).length > 0 ? normalized : null;
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
+
export function normalizeChatRequestFollowUp(input = {}) {
|
|
78
|
+
if (!input || typeof input !== 'object' || Array.isArray(input)) return null;
|
|
79
|
+
const normalized = {
|
|
80
|
+
...(normalizeText(input.sessionKey, null) ? { sessionKey: normalizeText(input.sessionKey, null) } : {}),
|
|
81
|
+
};
|
|
82
|
+
return Object.keys(normalized).length > 0 ? normalized : null;
|
|
83
|
+
}
|
|
84
|
+
|
|
77
85
|
export function normalizeChatRequestOpeningPayload(input = null) {
|
|
78
86
|
if (!input || typeof input !== 'object' || Array.isArray(input)) return null;
|
|
79
87
|
const payload = cloneJsonObject(input);
|
|
@@ -189,6 +197,7 @@ export function normalizeChatRequestInput({ requestContext = {}, source = null }
|
|
|
189
197
|
});
|
|
190
198
|
const openingPayload = normalizeChatRequestOpeningPayload(kickoffBrief?.payload ?? normalizedContext.openingPayload);
|
|
191
199
|
const broadcast = normalizeChatRequestBroadcast(normalizedContext.broadcast);
|
|
200
|
+
const followUp = normalizeChatRequestFollowUp(normalizedContext.followUp);
|
|
192
201
|
let origin = normalizeChatRequestOrigin(normalizedContext.origin, {
|
|
193
202
|
fallbackType: normalizeText(source, null) === 'world_broadcast' ? 'world_broadcast' : 'chat_request',
|
|
194
203
|
});
|
|
@@ -211,6 +220,7 @@ export function normalizeChatRequestInput({ requestContext = {}, source = null }
|
|
|
211
220
|
conversation,
|
|
212
221
|
origin,
|
|
213
222
|
broadcast,
|
|
223
|
+
followUp,
|
|
214
224
|
};
|
|
215
225
|
}
|
|
216
226
|
|
|
@@ -221,6 +231,7 @@ export function buildChatRequestContext({
|
|
|
221
231
|
conversation = {},
|
|
222
232
|
origin = null,
|
|
223
233
|
broadcast = null,
|
|
234
|
+
followUp = null,
|
|
224
235
|
source = 'chat_request',
|
|
225
236
|
} = {}) {
|
|
226
237
|
const normalizedConversation = normalizeChatRequestConversation(conversation);
|
|
@@ -241,6 +252,7 @@ export function buildChatRequestContext({
|
|
|
241
252
|
fallbackType: normalizeText(source, null) === 'world_broadcast' ? 'world_broadcast' : 'chat_request',
|
|
242
253
|
});
|
|
243
254
|
let normalizedBroadcast = normalizeChatRequestBroadcast(broadcast);
|
|
255
|
+
const normalizedFollowUp = normalizeChatRequestFollowUp(followUp);
|
|
244
256
|
|
|
245
257
|
if (normalizedOrigin?.broadcastId && !normalizedBroadcast?.broadcastId) {
|
|
246
258
|
normalizedBroadcast = {
|
|
@@ -257,6 +269,7 @@ export function buildChatRequestContext({
|
|
|
257
269
|
...(Object.keys(normalizedConversation).length > 0 ? { conversation: normalizedConversation } : {}),
|
|
258
270
|
...(normalizedOrigin ? { origin: normalizedOrigin } : {}),
|
|
259
271
|
...(normalizedBroadcast ? { broadcast: normalizedBroadcast } : {}),
|
|
272
|
+
...(normalizedFollowUp ? { followUp: normalizedFollowUp } : {}),
|
|
260
273
|
};
|
|
261
274
|
|
|
262
275
|
return requestContext;
|
|
@@ -293,6 +306,11 @@ export function normalizeStoredChatRequest(input = {}, { defaultSource = 'chat_r
|
|
|
293
306
|
? input.broadcast
|
|
294
307
|
: normalizedRequest.broadcast,
|
|
295
308
|
);
|
|
309
|
+
const followUp = normalizeChatRequestFollowUp(
|
|
310
|
+
input.followUp && typeof input.followUp === 'object' && !Array.isArray(input.followUp)
|
|
311
|
+
? input.followUp
|
|
312
|
+
: normalizedRequest.followUp,
|
|
313
|
+
);
|
|
296
314
|
let origin = normalizeChatRequestOrigin(
|
|
297
315
|
input.origin && typeof input.origin === 'object' && !Array.isArray(input.origin)
|
|
298
316
|
? input.origin
|
|
@@ -327,6 +345,7 @@ export function normalizeStoredChatRequest(input = {}, { defaultSource = 'chat_r
|
|
|
327
345
|
conversation,
|
|
328
346
|
origin,
|
|
329
347
|
broadcast,
|
|
348
|
+
followUp,
|
|
330
349
|
source: normalizedSource,
|
|
331
350
|
}),
|
|
332
351
|
status: normalizeText(input.status, 'pending'),
|
|
@@ -189,8 +189,10 @@ export function createAcceptedChatKickoffRuntimeContextForAgent(bundle = {}, {
|
|
|
189
189
|
export function formatAcceptedChatKickoffMessage(bundle = {}, { viewer = 'recipient' } = {}) {
|
|
190
190
|
const normalizedViewer = viewer === 'sender' ? 'sender' : 'recipient';
|
|
191
191
|
const requestContext = bundle.requestContext && typeof bundle.requestContext === 'object' && !Array.isArray(bundle.requestContext)
|
|
192
|
-
? bundle.requestContext
|
|
192
|
+
? cloneJsonObject(bundle.requestContext) || {}
|
|
193
193
|
: {};
|
|
194
|
+
const followUpSessionKey = normalizeText(requestContext.followUp?.sessionKey, null);
|
|
195
|
+
if (requestContext.followUp) delete requestContext.followUp;
|
|
194
196
|
const worldInfo = bundle.worldInfo && typeof bundle.worldInfo === 'object' && !Array.isArray(bundle.worldInfo)
|
|
195
197
|
? bundle.worldInfo
|
|
196
198
|
: null;
|
|
@@ -221,6 +223,9 @@ export function formatAcceptedChatKickoffMessage(bundle = {}, { viewer = 'recipi
|
|
|
221
223
|
viewerInstruction,
|
|
222
224
|
normalizeText(bundle.requestId, null) ? `Accepted episode: ${bundle.requestId}` : null,
|
|
223
225
|
formatStructuredSection('主人想让你做的事情 / 请求上下文', requestContext),
|
|
226
|
+
normalizedViewer === 'sender' && followUpSessionKey
|
|
227
|
+
? `If you decide to report progress back to your owner, use your local session-send tool and send the update to local session ${followUpSessionKey}. Do not report every turn. Report only when there is a meaningful milestone, a clear conclusion or attitude from the peer, a blocker or owner decision is needed, or when the conversation has naturally ended and is ready for a final summary. Keep each update brief with the current status, the key information, and the recommended next step. If no update is needed yet, you may wait.`
|
|
228
|
+
: null,
|
|
224
229
|
formatStructuredSection('世界信息', worldInfo),
|
|
225
230
|
formatStructuredSection('我方信息', selfInfo),
|
|
226
231
|
formatStructuredSection('对方信息', peerInfo),
|
|
@@ -316,6 +316,20 @@ export function parseJsonDocument(text, fallback = null) {
|
|
|
316
316
|
return fallback;
|
|
317
317
|
}
|
|
318
318
|
|
|
319
|
+
function parseCommandJsonOutput(result = {}, fallback = null) {
|
|
320
|
+
const stdout = String(result?.stdout || '').trim();
|
|
321
|
+
const stderr = String(result?.stderr || '').trim();
|
|
322
|
+
const combined = [stdout, stderr].filter(Boolean).join('\n');
|
|
323
|
+
const reversed = [stderr, stdout].filter(Boolean).join('\n');
|
|
324
|
+
return (
|
|
325
|
+
parseJsonDocument(stdout, null)
|
|
326
|
+
|| parseJsonDocument(stderr, null)
|
|
327
|
+
|| parseJsonDocument(combined, null)
|
|
328
|
+
|| parseJsonDocument(reversed, null)
|
|
329
|
+
|| fallback
|
|
330
|
+
);
|
|
331
|
+
}
|
|
332
|
+
|
|
319
333
|
function parseLegacyChannelTokenStatus(tokenValue = '') {
|
|
320
334
|
const normalized = normalizeText(tokenValue, '').toLowerCase();
|
|
321
335
|
if (!normalized || normalized === 'missing' || normalized === 'none' || normalized === 'unset') {
|
|
@@ -1089,7 +1103,7 @@ export async function readGatewayStatus({
|
|
|
1089
1103
|
env: buildOpenclawCommandEnv({ configPath, stateDir, env }),
|
|
1090
1104
|
dryRun,
|
|
1091
1105
|
});
|
|
1092
|
-
const payload =
|
|
1106
|
+
const payload = parseCommandJsonOutput(result, null);
|
|
1093
1107
|
if (!payload) {
|
|
1094
1108
|
throw createInstallerError(
|
|
1095
1109
|
'invalid_gateway_status',
|
|
@@ -1118,7 +1132,7 @@ export async function readChannelStatus({
|
|
|
1118
1132
|
dryRun,
|
|
1119
1133
|
});
|
|
1120
1134
|
const output = `${result.stdout || ''}\n${result.stderr || ''}`;
|
|
1121
|
-
const payload =
|
|
1135
|
+
const payload = parseCommandJsonOutput(result, null) || parseLegacyChannelStatus(output);
|
|
1122
1136
|
if (!payload) {
|
|
1123
1137
|
throw createInstallerError(
|
|
1124
1138
|
'invalid_channel_status',
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
defaultClaworldAccountId,
|
|
11
11
|
inspectClaworldChannelAccount,
|
|
12
12
|
listClaworldAccountIds,
|
|
13
|
+
projectClaworldStatusAccount,
|
|
13
14
|
resolveClaworldChannelAccount,
|
|
14
15
|
resolveClaworldRuntimeConfig,
|
|
15
16
|
validateClaworldChannelConfig,
|
|
@@ -61,6 +62,11 @@ function normalizeRelayHttpBaseUrl(serverUrl) {
|
|
|
61
62
|
return parsed.toString().replace(/\/$/, '');
|
|
62
63
|
}
|
|
63
64
|
|
|
65
|
+
function normalizePluginOptionalText(value) {
|
|
66
|
+
const normalized = String(value ?? '').trim();
|
|
67
|
+
return normalized || null;
|
|
68
|
+
}
|
|
69
|
+
|
|
64
70
|
function inferRelayDomain(runtimeConfig = {}) {
|
|
65
71
|
const defaultToAddress = String(runtimeConfig.relay?.defaultToAddress || '').trim();
|
|
66
72
|
const atIndex = defaultToAddress.indexOf('@');
|
|
@@ -611,6 +617,7 @@ async function createChatRequest({
|
|
|
611
617
|
targetAgentId,
|
|
612
618
|
openingMessage = null,
|
|
613
619
|
worldId = null,
|
|
620
|
+
requestContext = null,
|
|
614
621
|
fetchImpl,
|
|
615
622
|
}) {
|
|
616
623
|
const normalizedTargetAgentId = normalizeClaworldText(targetAgentId, null);
|
|
@@ -638,6 +645,9 @@ async function createChatRequest({
|
|
|
638
645
|
targetAgentId: normalizedTargetAgentId,
|
|
639
646
|
openingMessage: normalizeClaworldText(openingMessage, null),
|
|
640
647
|
...(normalizeClaworldText(worldId, null) ? { worldId: normalizeClaworldText(worldId, null) } : {}),
|
|
648
|
+
...(requestContext && typeof requestContext === 'object' && !Array.isArray(requestContext)
|
|
649
|
+
? { requestContext }
|
|
650
|
+
: {}),
|
|
641
651
|
}),
|
|
642
652
|
});
|
|
643
653
|
if (!result.ok) {
|
|
@@ -652,7 +662,7 @@ async function createChatRequest({
|
|
|
652
662
|
return result.body || {};
|
|
653
663
|
}
|
|
654
664
|
|
|
655
|
-
async function
|
|
665
|
+
async function listChatInbox({
|
|
656
666
|
runtimeConfig,
|
|
657
667
|
agentId,
|
|
658
668
|
direction = null,
|
|
@@ -1301,7 +1311,12 @@ function createDeliveryReplyDispatcher({
|
|
|
1301
1311
|
: undefined;
|
|
1302
1312
|
|
|
1303
1313
|
let replied = false;
|
|
1314
|
+
let keptSilent = false;
|
|
1304
1315
|
let suppressed = false;
|
|
1316
|
+
let replyTransport = null;
|
|
1317
|
+
let replyFallbackUsed = false;
|
|
1318
|
+
let keptSilentTransport = null;
|
|
1319
|
+
let keptSilentFallbackUsed = false;
|
|
1305
1320
|
const finalTexts = [];
|
|
1306
1321
|
const blockTexts = [];
|
|
1307
1322
|
let partialContinuationText = '';
|
|
@@ -1383,16 +1398,52 @@ function createDeliveryReplyDispatcher({
|
|
|
1383
1398
|
suppressed = true;
|
|
1384
1399
|
return false;
|
|
1385
1400
|
}
|
|
1386
|
-
relayClient.
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1401
|
+
if (typeof relayClient.sendReplyAndWaitForAck === 'function') {
|
|
1402
|
+
const replyResult = await relayClient.sendReplyAndWaitForAck({
|
|
1403
|
+
deliveryId,
|
|
1404
|
+
sessionKey,
|
|
1405
|
+
replyText: normalized,
|
|
1406
|
+
source: 'openclaw-autochain',
|
|
1407
|
+
});
|
|
1408
|
+
replyTransport = replyResult?.transport || 'websocket';
|
|
1409
|
+
replyFallbackUsed = replyResult?.fallbackUsed === true;
|
|
1410
|
+
} else {
|
|
1411
|
+
relayClient.sendReply({
|
|
1412
|
+
deliveryId,
|
|
1413
|
+
sessionKey,
|
|
1414
|
+
replyText: normalized,
|
|
1415
|
+
source: 'openclaw-autochain',
|
|
1416
|
+
});
|
|
1417
|
+
replyTransport = 'websocket-fire-and-forget';
|
|
1418
|
+
replyFallbackUsed = false;
|
|
1419
|
+
}
|
|
1392
1420
|
replied = true;
|
|
1393
1421
|
return true;
|
|
1394
1422
|
};
|
|
1395
1423
|
|
|
1424
|
+
const flushKeptSilent = async (reason = null) => {
|
|
1425
|
+
if (replied || keptSilent || suppressed) return false;
|
|
1426
|
+
if (allowReply === false) {
|
|
1427
|
+
suppressed = true;
|
|
1428
|
+
return false;
|
|
1429
|
+
}
|
|
1430
|
+
if (typeof relayClient.sendKeepSilentAndWaitForAck === 'function') {
|
|
1431
|
+
const silentResult = await relayClient.sendKeepSilentAndWaitForAck({
|
|
1432
|
+
deliveryId,
|
|
1433
|
+
sessionKey,
|
|
1434
|
+
reason: normalizePluginOptionalText(reason) || 'no_renderable_reply',
|
|
1435
|
+
source: 'openclaw-autochain',
|
|
1436
|
+
});
|
|
1437
|
+
keptSilentTransport = silentResult?.transport || 'websocket';
|
|
1438
|
+
keptSilentFallbackUsed = silentResult?.fallbackUsed === true;
|
|
1439
|
+
} else {
|
|
1440
|
+
keptSilentTransport = 'unsupported';
|
|
1441
|
+
keptSilentFallbackUsed = false;
|
|
1442
|
+
}
|
|
1443
|
+
keptSilent = true;
|
|
1444
|
+
return true;
|
|
1445
|
+
};
|
|
1446
|
+
|
|
1396
1447
|
const dispatchApi = runtime.channel.reply.createReplyDispatcherWithTyping({
|
|
1397
1448
|
responsePrefix: prefixContext.responsePrefix,
|
|
1398
1449
|
responsePrefixContextProvider: prefixContext.responsePrefixContextProvider,
|
|
@@ -1435,6 +1486,8 @@ function createDeliveryReplyDispatcher({
|
|
|
1435
1486
|
: null;
|
|
1436
1487
|
if (continuation.text) {
|
|
1437
1488
|
await flushReply(continuation.text);
|
|
1489
|
+
} else {
|
|
1490
|
+
await flushKeptSilent(continuation.source);
|
|
1438
1491
|
}
|
|
1439
1492
|
}
|
|
1440
1493
|
await dispatchApi.markDispatchIdle?.();
|
|
@@ -1472,6 +1525,7 @@ function createDeliveryReplyDispatcher({
|
|
|
1472
1525
|
},
|
|
1473
1526
|
markDispatchIdle,
|
|
1474
1527
|
didReply: () => replied,
|
|
1528
|
+
didKeepSilent: () => keptSilent,
|
|
1475
1529
|
getRuntimeOutputSummary: () => ({
|
|
1476
1530
|
counts: { ...runtimeOutputSummary.counts },
|
|
1477
1531
|
previews: {
|
|
@@ -1484,6 +1538,10 @@ function createDeliveryReplyDispatcher({
|
|
|
1484
1538
|
},
|
|
1485
1539
|
relayContinuationSource: runtimeOutputSummary.relayContinuationSource,
|
|
1486
1540
|
relayContinuationPreview: runtimeOutputSummary.relayContinuationPreview,
|
|
1541
|
+
replyTransport,
|
|
1542
|
+
replyFallbackUsed,
|
|
1543
|
+
keptSilentTransport,
|
|
1544
|
+
keptSilentFallbackUsed,
|
|
1487
1545
|
}),
|
|
1488
1546
|
};
|
|
1489
1547
|
}
|
|
@@ -1500,7 +1558,14 @@ async function runDeliveryReplyDispatch({
|
|
|
1500
1558
|
runtimeAccountId,
|
|
1501
1559
|
inboundCtx,
|
|
1502
1560
|
} = {}) {
|
|
1503
|
-
const {
|
|
1561
|
+
const {
|
|
1562
|
+
dispatcher,
|
|
1563
|
+
replyOptions,
|
|
1564
|
+
markDispatchIdle,
|
|
1565
|
+
didReply,
|
|
1566
|
+
didKeepSilent,
|
|
1567
|
+
getRuntimeOutputSummary,
|
|
1568
|
+
} = createDeliveryReplyDispatcher({
|
|
1504
1569
|
runtime,
|
|
1505
1570
|
currentCfg,
|
|
1506
1571
|
relayClient,
|
|
@@ -1523,6 +1588,7 @@ async function runDeliveryReplyDispatch({
|
|
|
1523
1588
|
return {
|
|
1524
1589
|
dispatchResult,
|
|
1525
1590
|
replied: didReply(),
|
|
1591
|
+
keptSilent: didKeepSilent(),
|
|
1526
1592
|
runtimeOutputSummary: getRuntimeOutputSummary(),
|
|
1527
1593
|
};
|
|
1528
1594
|
}
|
|
@@ -1704,6 +1770,7 @@ async function maybeBridgeRuntimeDelivery({
|
|
|
1704
1770
|
let {
|
|
1705
1771
|
dispatchResult,
|
|
1706
1772
|
replied,
|
|
1773
|
+
keptSilent,
|
|
1707
1774
|
runtimeOutputSummary,
|
|
1708
1775
|
} = await runDeliveryReplyDispatch({
|
|
1709
1776
|
runtime,
|
|
@@ -1747,6 +1814,7 @@ async function maybeBridgeRuntimeDelivery({
|
|
|
1747
1814
|
({
|
|
1748
1815
|
dispatchResult,
|
|
1749
1816
|
replied,
|
|
1817
|
+
keptSilent,
|
|
1750
1818
|
runtimeOutputSummary,
|
|
1751
1819
|
} = await runDeliveryReplyDispatch({
|
|
1752
1820
|
runtime,
|
|
@@ -1767,6 +1835,7 @@ async function maybeBridgeRuntimeDelivery({
|
|
|
1767
1835
|
sessionKey,
|
|
1768
1836
|
queuedFinal: Boolean(dispatchResult?.queuedFinal),
|
|
1769
1837
|
replied,
|
|
1838
|
+
keptSilent,
|
|
1770
1839
|
routeStatus: routed?.status || null,
|
|
1771
1840
|
runtimeOutputSummary,
|
|
1772
1841
|
});
|
|
@@ -1775,6 +1844,7 @@ async function maybeBridgeRuntimeDelivery({
|
|
|
1775
1844
|
skipped: false,
|
|
1776
1845
|
ok: true,
|
|
1777
1846
|
replied,
|
|
1847
|
+
keptSilent,
|
|
1778
1848
|
queuedFinal: Boolean(dispatchResult?.queuedFinal),
|
|
1779
1849
|
sessionKey,
|
|
1780
1850
|
routeStatus: routed?.status || null,
|
|
@@ -1794,6 +1864,69 @@ export function createClaworldChannelPlugin({
|
|
|
1794
1864
|
const relayClients = new Map();
|
|
1795
1865
|
const lifecycles = new Map();
|
|
1796
1866
|
const accountRuntimeContexts = new Map();
|
|
1867
|
+
const accountBindingStates = new Map();
|
|
1868
|
+
|
|
1869
|
+
function resolveAccountBindingKey(runtimeConfig = {}, fallbackAccountId = 'default') {
|
|
1870
|
+
return String(runtimeConfig?.accountId || fallbackAccountId || 'default');
|
|
1871
|
+
}
|
|
1872
|
+
|
|
1873
|
+
function mergeBoundRuntimeConfig(currentRuntimeConfig = {}, boundRuntimeConfig = {}) {
|
|
1874
|
+
return applyRuntimeIdentity({
|
|
1875
|
+
...currentRuntimeConfig,
|
|
1876
|
+
...boundRuntimeConfig,
|
|
1877
|
+
relay: {
|
|
1878
|
+
...(currentRuntimeConfig?.relay && typeof currentRuntimeConfig.relay === 'object' ? currentRuntimeConfig.relay : {}),
|
|
1879
|
+
...(boundRuntimeConfig?.relay && typeof boundRuntimeConfig.relay === 'object' ? boundRuntimeConfig.relay : {}),
|
|
1880
|
+
},
|
|
1881
|
+
});
|
|
1882
|
+
}
|
|
1883
|
+
|
|
1884
|
+
async function ensureAccountRelayBinding({ runtimeConfig, accountId = null }) {
|
|
1885
|
+
const normalizedRuntimeConfig = applyRuntimeIdentity(runtimeConfig);
|
|
1886
|
+
const accountKey = resolveAccountBindingKey(normalizedRuntimeConfig, accountId || null);
|
|
1887
|
+
const cachedState = accountBindingStates.get(accountKey) || null;
|
|
1888
|
+
const cachedBinding = cachedState?.binding || null;
|
|
1889
|
+
|
|
1890
|
+
if (
|
|
1891
|
+
cachedBinding
|
|
1892
|
+
&& cachedBinding.runtimeConfig?.serverUrl
|
|
1893
|
+
&& cachedBinding.runtimeConfig.serverUrl === normalizedRuntimeConfig.serverUrl
|
|
1894
|
+
) {
|
|
1895
|
+
return {
|
|
1896
|
+
...cachedBinding,
|
|
1897
|
+
runtimeConfig: mergeBoundRuntimeConfig(normalizedRuntimeConfig, cachedBinding.runtimeConfig),
|
|
1898
|
+
bindingSource: cachedBinding.bindingSource === 'configured_app_token'
|
|
1899
|
+
? 'configured_app_token'
|
|
1900
|
+
: 'binding_cache',
|
|
1901
|
+
};
|
|
1902
|
+
}
|
|
1903
|
+
|
|
1904
|
+
if (cachedState?.promise) {
|
|
1905
|
+
return cachedState.promise;
|
|
1906
|
+
}
|
|
1907
|
+
|
|
1908
|
+
const promise = ensureRelayBinding({
|
|
1909
|
+
runtimeConfig: normalizedRuntimeConfig,
|
|
1910
|
+
fetchImpl,
|
|
1911
|
+
logger,
|
|
1912
|
+
}).then((binding) => {
|
|
1913
|
+
const resolvedBinding = {
|
|
1914
|
+
...binding,
|
|
1915
|
+
runtimeConfig: mergeBoundRuntimeConfig(normalizedRuntimeConfig, binding.runtimeConfig),
|
|
1916
|
+
};
|
|
1917
|
+
accountBindingStates.set(accountKey, { binding: resolvedBinding });
|
|
1918
|
+
return resolvedBinding;
|
|
1919
|
+
}).catch((error) => {
|
|
1920
|
+
const latest = accountBindingStates.get(accountKey) || null;
|
|
1921
|
+
if (latest?.promise === promise) {
|
|
1922
|
+
accountBindingStates.delete(accountKey);
|
|
1923
|
+
}
|
|
1924
|
+
throw error;
|
|
1925
|
+
});
|
|
1926
|
+
|
|
1927
|
+
accountBindingStates.set(accountKey, { promise });
|
|
1928
|
+
return promise;
|
|
1929
|
+
}
|
|
1797
1930
|
|
|
1798
1931
|
async function persistRuntimeAppToken({ runtime, accountId, appToken }) {
|
|
1799
1932
|
if (!runtime?.config?.loadConfig || !runtime?.config?.writeConfigFile) {
|
|
@@ -1847,7 +1980,7 @@ export function createClaworldChannelPlugin({
|
|
|
1847
1980
|
bindingSource: 'runtime_context',
|
|
1848
1981
|
};
|
|
1849
1982
|
}
|
|
1850
|
-
const binding = await
|
|
1983
|
+
const binding = await ensureAccountRelayBinding({ runtimeConfig, accountId });
|
|
1851
1984
|
runtimeConfig = binding.runtimeConfig;
|
|
1852
1985
|
return {
|
|
1853
1986
|
...context,
|
|
@@ -1896,7 +2029,7 @@ export function createClaworldChannelPlugin({
|
|
|
1896
2029
|
|
|
1897
2030
|
let binding;
|
|
1898
2031
|
try {
|
|
1899
|
-
binding = await
|
|
2032
|
+
binding = await ensureAccountRelayBinding({ runtimeConfig, accountId: runtimeAccountId });
|
|
1900
2033
|
} catch (error) {
|
|
1901
2034
|
const normalized = normalizeRuntimeBoundaryError(error, {
|
|
1902
2035
|
code: 'claworld_relay_binding_failed',
|
|
@@ -2059,6 +2192,7 @@ export function createClaworldChannelPlugin({
|
|
|
2059
2192
|
relayClients.delete(accountKey);
|
|
2060
2193
|
}
|
|
2061
2194
|
accountRuntimeContexts.delete(accountKey);
|
|
2195
|
+
accountBindingStates.delete(accountKey);
|
|
2062
2196
|
},
|
|
2063
2197
|
});
|
|
2064
2198
|
|
|
@@ -2200,7 +2334,8 @@ export function createClaworldChannelPlugin({
|
|
|
2200
2334
|
validate: validateClaworldChannelConfig,
|
|
2201
2335
|
listAccountIds: (cfg) => listClaworldAccountIds(cfg),
|
|
2202
2336
|
defaultAccountId: (cfg) => defaultClaworldAccountId(cfg),
|
|
2203
|
-
inspectAccount: (cfg, accountId) =>
|
|
2337
|
+
inspectAccount: (cfg, accountId) =>
|
|
2338
|
+
projectClaworldStatusAccount(inspectClaworldChannelAccount(cfg, accountId)),
|
|
2204
2339
|
resolveAccount: (cfg, accountId) => resolveClaworldChannelAccount(cfg, accountId),
|
|
2205
2340
|
resolveRuntimeConfig: (cfg, accountId) => resolveClaworldRuntimeConfig(cfg, accountId),
|
|
2206
2341
|
isConfigured: (account, cfg) => {
|
|
@@ -2360,18 +2495,35 @@ export function createClaworldChannelPlugin({
|
|
|
2360
2495
|
},
|
|
2361
2496
|
requestChat: async (context = {}) => {
|
|
2362
2497
|
const resolvedContext = await resolveBoundRuntimeContext(context);
|
|
2498
|
+
const requestContext = resolvedContext.requesterSessionKey
|
|
2499
|
+
? {
|
|
2500
|
+
followUp: {
|
|
2501
|
+
sessionKey: resolvedContext.requesterSessionKey,
|
|
2502
|
+
},
|
|
2503
|
+
}
|
|
2504
|
+
: null;
|
|
2363
2505
|
return createChatRequest({
|
|
2364
2506
|
runtimeConfig: resolvedContext.runtimeConfig,
|
|
2365
2507
|
fromAgentId: resolvedContext.agentId || null,
|
|
2366
2508
|
targetAgentId: context.targetAgentId || null,
|
|
2367
2509
|
openingMessage: context.openingMessage || context.message || context.text || null,
|
|
2368
2510
|
worldId: context.worldId || null,
|
|
2511
|
+
requestContext,
|
|
2512
|
+
fetchImpl,
|
|
2513
|
+
});
|
|
2514
|
+
},
|
|
2515
|
+
listChatInbox: async (context = {}) => {
|
|
2516
|
+
const resolvedContext = await resolveBoundRuntimeContext(context);
|
|
2517
|
+
return listChatInbox({
|
|
2518
|
+
runtimeConfig: resolvedContext.runtimeConfig,
|
|
2519
|
+
agentId: resolvedContext.agentId || null,
|
|
2520
|
+
direction: context.direction || null,
|
|
2369
2521
|
fetchImpl,
|
|
2370
2522
|
});
|
|
2371
2523
|
},
|
|
2372
2524
|
listChatRequests: async (context = {}) => {
|
|
2373
2525
|
const resolvedContext = await resolveBoundRuntimeContext(context);
|
|
2374
|
-
return
|
|
2526
|
+
return listChatInbox({
|
|
2375
2527
|
runtimeConfig: resolvedContext.runtimeConfig,
|
|
2376
2528
|
agentId: resolvedContext.agentId || null,
|
|
2377
2529
|
direction: context.direction || null,
|
|
@@ -417,6 +417,14 @@ export function inspectClaworldChannelAccount(config = {}, accountId = null) {
|
|
|
417
417
|
};
|
|
418
418
|
}
|
|
419
419
|
|
|
420
|
+
export function projectClaworldStatusAccount(inspection = {}) {
|
|
421
|
+
// Keep the steady-state credential nested under relay/runtimeConfig so
|
|
422
|
+
// generic OpenClaw status does not misclassify Claworld as a bot+app token
|
|
423
|
+
// channel.
|
|
424
|
+
const { appToken: _appToken, ...statusAccount } = inspection || {};
|
|
425
|
+
return statusAccount;
|
|
426
|
+
}
|
|
427
|
+
|
|
420
428
|
export function resolveClaworldRuntimeConfig(config = {}, accountId = null) {
|
|
421
429
|
const result = validateClaworldChannelConfig(config, accountId);
|
|
422
430
|
if (!result.ok) {
|
|
@@ -435,11 +443,8 @@ export function resolveClaworldRuntimeConfig(config = {}, accountId = null) {
|
|
|
435
443
|
export function resolveClaworldChannelAccount(config = {}, accountId = null) {
|
|
436
444
|
const runtimeConfig = resolveClaworldRuntimeConfig(config, accountId);
|
|
437
445
|
const inspection = inspectClaworldChannelAccount(config, accountId);
|
|
438
|
-
// Keep the steady-state credential nested under relay/runtimeConfig so generic
|
|
439
|
-
// OpenClaw status does not misclassify Claworld as a bot+app token channel.
|
|
440
|
-
const { appToken: _appToken, ...statusAccount } = inspection;
|
|
441
446
|
return {
|
|
442
|
-
...
|
|
447
|
+
...projectClaworldStatusAccount(inspection),
|
|
443
448
|
runtimeReady: true,
|
|
444
449
|
resolvedFrom: accountId ? 'requested_account' : 'default_account',
|
|
445
450
|
runtimeConfig,
|