@xfxstudio/claworld 0.2.6 → 0.2.8
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/src/openclaw/installer/core.js +16 -2
- package/src/openclaw/installer/doctor.js +63 -0
- package/src/openclaw/plugin/claworld-channel-plugin.js +189 -18
- package/src/openclaw/plugin/config-schema.js +9 -4
- package/src/openclaw/plugin/managed-config.js +110 -0
- package/src/openclaw/plugin/register.js +187 -61
- package/src/openclaw/plugin/relay-client.js +511 -0
- package/src/openclaw/runtime/demo-session-bootstrap.js +1 -2
- package/src/openclaw/runtime/tool-contracts.js +1 -1
- package/src/openclaw/runtime/tool-inventory.js +2 -1
- package/src/openclaw/runtime/world-moderation-helper.js +10 -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-service.js +1 -0
- package/src/product-shell/worlds/world-admin-service.js +52 -32
- package/src/product-shell/worlds/world-authorization.js +15 -1
- package/src/product-shell/worlds/world-routes.js +1 -0
- 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
|
@@ -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',
|
|
@@ -6,8 +6,11 @@ import {
|
|
|
6
6
|
DEFAULT_CLAWORLD_AGENT_ID,
|
|
7
7
|
DEFAULT_CLAWORLD_SERVER_URL,
|
|
8
8
|
expandUserPath,
|
|
9
|
+
getEffectiveAgentSandboxMode,
|
|
10
|
+
MIN_MANAGED_SESSION_VISIBILITY,
|
|
9
11
|
normalizeText,
|
|
10
12
|
resolveClaworldManagedRuntimeOptions,
|
|
13
|
+
sandboxModeNeedsSessionToolsVisibility,
|
|
11
14
|
} from '../plugin/managed-config.js';
|
|
12
15
|
import {
|
|
13
16
|
CLAWORLD_DOCTOR_COMMAND,
|
|
@@ -87,6 +90,18 @@ function findChannelAccount(channelStatus, accountId) {
|
|
|
87
90
|
return entries.find((entry) => entry?.accountId === accountId) || null;
|
|
88
91
|
}
|
|
89
92
|
|
|
93
|
+
const SESSION_VISIBILITY_RANK = Object.freeze({
|
|
94
|
+
self: 0,
|
|
95
|
+
tree: 1,
|
|
96
|
+
agent: 2,
|
|
97
|
+
all: 3,
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
function resolveConfiguredSessionVisibility(config = {}) {
|
|
101
|
+
const visibility = normalizeText(config?.tools?.sessions?.visibility, null);
|
|
102
|
+
return Object.prototype.hasOwnProperty.call(SESSION_VISIBILITY_RANK, visibility) ? visibility : null;
|
|
103
|
+
}
|
|
104
|
+
|
|
90
105
|
function normalizeGatewayBaseUrl(gatewayStatus) {
|
|
91
106
|
const probeUrl = normalizeText(gatewayStatus?.gateway?.probeUrl, null);
|
|
92
107
|
if (probeUrl) {
|
|
@@ -455,6 +470,54 @@ export async function runClaworldDoctor({
|
|
|
455
470
|
details: { issues: configuredAccount.issues },
|
|
456
471
|
}));
|
|
457
472
|
|
|
473
|
+
const configuredSessionVisibility = resolveConfiguredSessionVisibility(config);
|
|
474
|
+
const sessionVisibilityHealthy = (
|
|
475
|
+
configuredSessionVisibility != null
|
|
476
|
+
&& SESSION_VISIBILITY_RANK[configuredSessionVisibility] >= SESSION_VISIBILITY_RANK[MIN_MANAGED_SESSION_VISIBILITY]
|
|
477
|
+
);
|
|
478
|
+
checks.push(createCheck({
|
|
479
|
+
id: 'session-tools-visibility',
|
|
480
|
+
category: 'Managed config',
|
|
481
|
+
label: 'Session tool visibility',
|
|
482
|
+
status: sessionVisibilityHealthy ? 'pass' : 'warn',
|
|
483
|
+
summary: sessionVisibilityHealthy
|
|
484
|
+
? `tools.sessions.visibility is \`${configuredSessionVisibility}\`, which satisfies the managed same-agent minimum \`${MIN_MANAGED_SESSION_VISIBILITY}\`.`
|
|
485
|
+
: `tools.sessions.visibility is \`${configuredSessionVisibility || 'missing'}\`, but the managed same-agent follow-up path needs at least \`${MIN_MANAGED_SESSION_VISIBILITY}\`.`,
|
|
486
|
+
action: sessionVisibilityHealthy
|
|
487
|
+
? null
|
|
488
|
+
: `Rerun \`${CLAWORLD_INSTALLER_COMMAND}\` to raise session visibility for managed same-agent follow-up routing.`,
|
|
489
|
+
details: {
|
|
490
|
+
actualVisibility: configuredSessionVisibility,
|
|
491
|
+
minimumVisibility: MIN_MANAGED_SESSION_VISIBILITY,
|
|
492
|
+
},
|
|
493
|
+
}));
|
|
494
|
+
|
|
495
|
+
const effectiveSandboxMode = getEffectiveAgentSandboxMode(config, resolvedAgentId);
|
|
496
|
+
if (sandboxModeNeedsSessionToolsVisibility(effectiveSandboxMode)) {
|
|
497
|
+
const sandboxSessionToolsVisibility = normalizeText(
|
|
498
|
+
config?.agents?.defaults?.sandbox?.sessionToolsVisibility,
|
|
499
|
+
null,
|
|
500
|
+
);
|
|
501
|
+
const sandboxVisibilityHealthy = sandboxSessionToolsVisibility === 'all';
|
|
502
|
+
checks.push(createCheck({
|
|
503
|
+
id: 'sandbox-session-tools-visibility',
|
|
504
|
+
category: 'Managed config',
|
|
505
|
+
label: 'Sandbox session tool visibility',
|
|
506
|
+
status: sandboxVisibilityHealthy ? 'pass' : 'warn',
|
|
507
|
+
summary: sandboxVisibilityHealthy
|
|
508
|
+
? `Effective sandbox mode is \`${effectiveSandboxMode}\` and agents.defaults.sandbox.sessionToolsVisibility is correctly set to \`all\`.`
|
|
509
|
+
: `Effective sandbox mode is \`${effectiveSandboxMode}\`, so agents.defaults.sandbox.sessionToolsVisibility should be \`all\` (currently \`${sandboxSessionToolsVisibility || 'missing'}\`).`,
|
|
510
|
+
action: sandboxVisibilityHealthy
|
|
511
|
+
? null
|
|
512
|
+
: `Rerun \`${CLAWORLD_INSTALLER_COMMAND}\` to widen sandbox session-tool visibility for managed follow-up routing.`,
|
|
513
|
+
details: {
|
|
514
|
+
effectiveSandboxMode,
|
|
515
|
+
sessionToolsVisibility: sandboxSessionToolsVisibility,
|
|
516
|
+
requiredSessionToolsVisibility: 'all',
|
|
517
|
+
},
|
|
518
|
+
}));
|
|
519
|
+
}
|
|
520
|
+
|
|
458
521
|
checks.push(createCheck({
|
|
459
522
|
id: 'app-token',
|
|
460
523
|
category: 'Credentials and runtime',
|
|
@@ -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('@');
|
|
@@ -710,6 +716,34 @@ async function acceptChatRequest({
|
|
|
710
716
|
return result.body || {};
|
|
711
717
|
}
|
|
712
718
|
|
|
719
|
+
async function rejectChatRequest({
|
|
720
|
+
runtimeConfig,
|
|
721
|
+
actorAgentId,
|
|
722
|
+
chatRequestId,
|
|
723
|
+
fetchImpl,
|
|
724
|
+
}) {
|
|
725
|
+
const baseUrl = normalizeRelayHttpBaseUrl(runtimeConfig.serverUrl);
|
|
726
|
+
const result = await fetchJson(fetchImpl, `${baseUrl}/v1/chat-requests/${encodeURIComponent(chatRequestId)}/reject`, {
|
|
727
|
+
method: 'POST',
|
|
728
|
+
headers: {
|
|
729
|
+
'content-type': 'application/json',
|
|
730
|
+
...(runtimeConfig.apiKey ? { 'x-api-key': runtimeConfig.apiKey } : {}),
|
|
731
|
+
...buildRuntimeAuthHeaders(runtimeConfig),
|
|
732
|
+
},
|
|
733
|
+
body: JSON.stringify({ actorAgentId }),
|
|
734
|
+
});
|
|
735
|
+
if (!result.ok) {
|
|
736
|
+
createRelayRouteError({
|
|
737
|
+
result,
|
|
738
|
+
runtimeConfig,
|
|
739
|
+
code: 'chat_request_reject_failed',
|
|
740
|
+
publicMessage: 'failed to reject chat request',
|
|
741
|
+
context: { actorAgentId, chatRequestId },
|
|
742
|
+
});
|
|
743
|
+
}
|
|
744
|
+
return result.body || {};
|
|
745
|
+
}
|
|
746
|
+
|
|
713
747
|
async function syncChatRequestApprovalPolicy({
|
|
714
748
|
runtimeConfig,
|
|
715
749
|
fetchImpl,
|
|
@@ -1301,7 +1335,12 @@ function createDeliveryReplyDispatcher({
|
|
|
1301
1335
|
: undefined;
|
|
1302
1336
|
|
|
1303
1337
|
let replied = false;
|
|
1338
|
+
let keptSilent = false;
|
|
1304
1339
|
let suppressed = false;
|
|
1340
|
+
let replyTransport = null;
|
|
1341
|
+
let replyFallbackUsed = false;
|
|
1342
|
+
let keptSilentTransport = null;
|
|
1343
|
+
let keptSilentFallbackUsed = false;
|
|
1305
1344
|
const finalTexts = [];
|
|
1306
1345
|
const blockTexts = [];
|
|
1307
1346
|
let partialContinuationText = '';
|
|
@@ -1383,16 +1422,52 @@ function createDeliveryReplyDispatcher({
|
|
|
1383
1422
|
suppressed = true;
|
|
1384
1423
|
return false;
|
|
1385
1424
|
}
|
|
1386
|
-
relayClient.
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1425
|
+
if (typeof relayClient.sendReplyAndWaitForAck === 'function') {
|
|
1426
|
+
const replyResult = await relayClient.sendReplyAndWaitForAck({
|
|
1427
|
+
deliveryId,
|
|
1428
|
+
sessionKey,
|
|
1429
|
+
replyText: normalized,
|
|
1430
|
+
source: 'openclaw-autochain',
|
|
1431
|
+
});
|
|
1432
|
+
replyTransport = replyResult?.transport || 'websocket';
|
|
1433
|
+
replyFallbackUsed = replyResult?.fallbackUsed === true;
|
|
1434
|
+
} else {
|
|
1435
|
+
relayClient.sendReply({
|
|
1436
|
+
deliveryId,
|
|
1437
|
+
sessionKey,
|
|
1438
|
+
replyText: normalized,
|
|
1439
|
+
source: 'openclaw-autochain',
|
|
1440
|
+
});
|
|
1441
|
+
replyTransport = 'websocket-fire-and-forget';
|
|
1442
|
+
replyFallbackUsed = false;
|
|
1443
|
+
}
|
|
1392
1444
|
replied = true;
|
|
1393
1445
|
return true;
|
|
1394
1446
|
};
|
|
1395
1447
|
|
|
1448
|
+
const flushKeptSilent = async (reason = null) => {
|
|
1449
|
+
if (replied || keptSilent || suppressed) return false;
|
|
1450
|
+
if (allowReply === false) {
|
|
1451
|
+
suppressed = true;
|
|
1452
|
+
return false;
|
|
1453
|
+
}
|
|
1454
|
+
if (typeof relayClient.sendKeepSilentAndWaitForAck === 'function') {
|
|
1455
|
+
const silentResult = await relayClient.sendKeepSilentAndWaitForAck({
|
|
1456
|
+
deliveryId,
|
|
1457
|
+
sessionKey,
|
|
1458
|
+
reason: normalizePluginOptionalText(reason) || 'no_renderable_reply',
|
|
1459
|
+
source: 'openclaw-autochain',
|
|
1460
|
+
});
|
|
1461
|
+
keptSilentTransport = silentResult?.transport || 'websocket';
|
|
1462
|
+
keptSilentFallbackUsed = silentResult?.fallbackUsed === true;
|
|
1463
|
+
} else {
|
|
1464
|
+
keptSilentTransport = 'unsupported';
|
|
1465
|
+
keptSilentFallbackUsed = false;
|
|
1466
|
+
}
|
|
1467
|
+
keptSilent = true;
|
|
1468
|
+
return true;
|
|
1469
|
+
};
|
|
1470
|
+
|
|
1396
1471
|
const dispatchApi = runtime.channel.reply.createReplyDispatcherWithTyping({
|
|
1397
1472
|
responsePrefix: prefixContext.responsePrefix,
|
|
1398
1473
|
responsePrefixContextProvider: prefixContext.responsePrefixContextProvider,
|
|
@@ -1421,6 +1496,7 @@ function createDeliveryReplyDispatcher({
|
|
|
1421
1496
|
});
|
|
1422
1497
|
|
|
1423
1498
|
const markDispatchIdle = async () => {
|
|
1499
|
+
await dispatchApi.dispatcher.waitForIdle?.();
|
|
1424
1500
|
if (!replied && !suppressed) {
|
|
1425
1501
|
const continuation = buildRelayContinuationText({
|
|
1426
1502
|
finalTexts,
|
|
@@ -1435,6 +1511,8 @@ function createDeliveryReplyDispatcher({
|
|
|
1435
1511
|
: null;
|
|
1436
1512
|
if (continuation.text) {
|
|
1437
1513
|
await flushReply(continuation.text);
|
|
1514
|
+
} else {
|
|
1515
|
+
await flushKeptSilent(continuation.source);
|
|
1438
1516
|
}
|
|
1439
1517
|
}
|
|
1440
1518
|
await dispatchApi.markDispatchIdle?.();
|
|
@@ -1472,6 +1550,7 @@ function createDeliveryReplyDispatcher({
|
|
|
1472
1550
|
},
|
|
1473
1551
|
markDispatchIdle,
|
|
1474
1552
|
didReply: () => replied,
|
|
1553
|
+
didKeepSilent: () => keptSilent,
|
|
1475
1554
|
getRuntimeOutputSummary: () => ({
|
|
1476
1555
|
counts: { ...runtimeOutputSummary.counts },
|
|
1477
1556
|
previews: {
|
|
@@ -1484,6 +1563,10 @@ function createDeliveryReplyDispatcher({
|
|
|
1484
1563
|
},
|
|
1485
1564
|
relayContinuationSource: runtimeOutputSummary.relayContinuationSource,
|
|
1486
1565
|
relayContinuationPreview: runtimeOutputSummary.relayContinuationPreview,
|
|
1566
|
+
replyTransport,
|
|
1567
|
+
replyFallbackUsed,
|
|
1568
|
+
keptSilentTransport,
|
|
1569
|
+
keptSilentFallbackUsed,
|
|
1487
1570
|
}),
|
|
1488
1571
|
};
|
|
1489
1572
|
}
|
|
@@ -1500,7 +1583,14 @@ async function runDeliveryReplyDispatch({
|
|
|
1500
1583
|
runtimeAccountId,
|
|
1501
1584
|
inboundCtx,
|
|
1502
1585
|
} = {}) {
|
|
1503
|
-
const {
|
|
1586
|
+
const {
|
|
1587
|
+
dispatcher,
|
|
1588
|
+
replyOptions,
|
|
1589
|
+
markDispatchIdle,
|
|
1590
|
+
didReply,
|
|
1591
|
+
didKeepSilent,
|
|
1592
|
+
getRuntimeOutputSummary,
|
|
1593
|
+
} = createDeliveryReplyDispatcher({
|
|
1504
1594
|
runtime,
|
|
1505
1595
|
currentCfg,
|
|
1506
1596
|
relayClient,
|
|
@@ -1523,6 +1613,7 @@ async function runDeliveryReplyDispatch({
|
|
|
1523
1613
|
return {
|
|
1524
1614
|
dispatchResult,
|
|
1525
1615
|
replied: didReply(),
|
|
1616
|
+
keptSilent: didKeepSilent(),
|
|
1526
1617
|
runtimeOutputSummary: getRuntimeOutputSummary(),
|
|
1527
1618
|
};
|
|
1528
1619
|
}
|
|
@@ -1704,6 +1795,7 @@ async function maybeBridgeRuntimeDelivery({
|
|
|
1704
1795
|
let {
|
|
1705
1796
|
dispatchResult,
|
|
1706
1797
|
replied,
|
|
1798
|
+
keptSilent,
|
|
1707
1799
|
runtimeOutputSummary,
|
|
1708
1800
|
} = await runDeliveryReplyDispatch({
|
|
1709
1801
|
runtime,
|
|
@@ -1747,6 +1839,7 @@ async function maybeBridgeRuntimeDelivery({
|
|
|
1747
1839
|
({
|
|
1748
1840
|
dispatchResult,
|
|
1749
1841
|
replied,
|
|
1842
|
+
keptSilent,
|
|
1750
1843
|
runtimeOutputSummary,
|
|
1751
1844
|
} = await runDeliveryReplyDispatch({
|
|
1752
1845
|
runtime,
|
|
@@ -1762,19 +1855,21 @@ async function maybeBridgeRuntimeDelivery({
|
|
|
1762
1855
|
}));
|
|
1763
1856
|
}
|
|
1764
1857
|
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1858
|
+
logger.info?.(`[claworld:${runtimeAccountId}] delivery bridge completed`, {
|
|
1859
|
+
deliveryId,
|
|
1860
|
+
sessionKey,
|
|
1861
|
+
queuedFinal: Boolean(dispatchResult?.queuedFinal),
|
|
1862
|
+
replied,
|
|
1863
|
+
keptSilent,
|
|
1864
|
+
routeStatus: routed?.status || null,
|
|
1865
|
+
runtimeOutputSummary,
|
|
1866
|
+
});
|
|
1773
1867
|
|
|
1774
1868
|
return {
|
|
1775
1869
|
skipped: false,
|
|
1776
1870
|
ok: true,
|
|
1777
1871
|
replied,
|
|
1872
|
+
keptSilent,
|
|
1778
1873
|
queuedFinal: Boolean(dispatchResult?.queuedFinal),
|
|
1779
1874
|
sessionKey,
|
|
1780
1875
|
routeStatus: routed?.status || null,
|
|
@@ -1794,6 +1889,69 @@ export function createClaworldChannelPlugin({
|
|
|
1794
1889
|
const relayClients = new Map();
|
|
1795
1890
|
const lifecycles = new Map();
|
|
1796
1891
|
const accountRuntimeContexts = new Map();
|
|
1892
|
+
const accountBindingStates = new Map();
|
|
1893
|
+
|
|
1894
|
+
function resolveAccountBindingKey(runtimeConfig = {}, fallbackAccountId = 'default') {
|
|
1895
|
+
return String(runtimeConfig?.accountId || fallbackAccountId || 'default');
|
|
1896
|
+
}
|
|
1897
|
+
|
|
1898
|
+
function mergeBoundRuntimeConfig(currentRuntimeConfig = {}, boundRuntimeConfig = {}) {
|
|
1899
|
+
return applyRuntimeIdentity({
|
|
1900
|
+
...currentRuntimeConfig,
|
|
1901
|
+
...boundRuntimeConfig,
|
|
1902
|
+
relay: {
|
|
1903
|
+
...(currentRuntimeConfig?.relay && typeof currentRuntimeConfig.relay === 'object' ? currentRuntimeConfig.relay : {}),
|
|
1904
|
+
...(boundRuntimeConfig?.relay && typeof boundRuntimeConfig.relay === 'object' ? boundRuntimeConfig.relay : {}),
|
|
1905
|
+
},
|
|
1906
|
+
});
|
|
1907
|
+
}
|
|
1908
|
+
|
|
1909
|
+
async function ensureAccountRelayBinding({ runtimeConfig, accountId = null }) {
|
|
1910
|
+
const normalizedRuntimeConfig = applyRuntimeIdentity(runtimeConfig);
|
|
1911
|
+
const accountKey = resolveAccountBindingKey(normalizedRuntimeConfig, accountId || null);
|
|
1912
|
+
const cachedState = accountBindingStates.get(accountKey) || null;
|
|
1913
|
+
const cachedBinding = cachedState?.binding || null;
|
|
1914
|
+
|
|
1915
|
+
if (
|
|
1916
|
+
cachedBinding
|
|
1917
|
+
&& cachedBinding.runtimeConfig?.serverUrl
|
|
1918
|
+
&& cachedBinding.runtimeConfig.serverUrl === normalizedRuntimeConfig.serverUrl
|
|
1919
|
+
) {
|
|
1920
|
+
return {
|
|
1921
|
+
...cachedBinding,
|
|
1922
|
+
runtimeConfig: mergeBoundRuntimeConfig(normalizedRuntimeConfig, cachedBinding.runtimeConfig),
|
|
1923
|
+
bindingSource: cachedBinding.bindingSource === 'configured_app_token'
|
|
1924
|
+
? 'configured_app_token'
|
|
1925
|
+
: 'binding_cache',
|
|
1926
|
+
};
|
|
1927
|
+
}
|
|
1928
|
+
|
|
1929
|
+
if (cachedState?.promise) {
|
|
1930
|
+
return cachedState.promise;
|
|
1931
|
+
}
|
|
1932
|
+
|
|
1933
|
+
const promise = ensureRelayBinding({
|
|
1934
|
+
runtimeConfig: normalizedRuntimeConfig,
|
|
1935
|
+
fetchImpl,
|
|
1936
|
+
logger,
|
|
1937
|
+
}).then((binding) => {
|
|
1938
|
+
const resolvedBinding = {
|
|
1939
|
+
...binding,
|
|
1940
|
+
runtimeConfig: mergeBoundRuntimeConfig(normalizedRuntimeConfig, binding.runtimeConfig),
|
|
1941
|
+
};
|
|
1942
|
+
accountBindingStates.set(accountKey, { binding: resolvedBinding });
|
|
1943
|
+
return resolvedBinding;
|
|
1944
|
+
}).catch((error) => {
|
|
1945
|
+
const latest = accountBindingStates.get(accountKey) || null;
|
|
1946
|
+
if (latest?.promise === promise) {
|
|
1947
|
+
accountBindingStates.delete(accountKey);
|
|
1948
|
+
}
|
|
1949
|
+
throw error;
|
|
1950
|
+
});
|
|
1951
|
+
|
|
1952
|
+
accountBindingStates.set(accountKey, { promise });
|
|
1953
|
+
return promise;
|
|
1954
|
+
}
|
|
1797
1955
|
|
|
1798
1956
|
async function persistRuntimeAppToken({ runtime, accountId, appToken }) {
|
|
1799
1957
|
if (!runtime?.config?.loadConfig || !runtime?.config?.writeConfigFile) {
|
|
@@ -1847,7 +2005,7 @@ export function createClaworldChannelPlugin({
|
|
|
1847
2005
|
bindingSource: 'runtime_context',
|
|
1848
2006
|
};
|
|
1849
2007
|
}
|
|
1850
|
-
const binding = await
|
|
2008
|
+
const binding = await ensureAccountRelayBinding({ runtimeConfig, accountId });
|
|
1851
2009
|
runtimeConfig = binding.runtimeConfig;
|
|
1852
2010
|
return {
|
|
1853
2011
|
...context,
|
|
@@ -1896,7 +2054,7 @@ export function createClaworldChannelPlugin({
|
|
|
1896
2054
|
|
|
1897
2055
|
let binding;
|
|
1898
2056
|
try {
|
|
1899
|
-
binding = await
|
|
2057
|
+
binding = await ensureAccountRelayBinding({ runtimeConfig, accountId: runtimeAccountId });
|
|
1900
2058
|
} catch (error) {
|
|
1901
2059
|
const normalized = normalizeRuntimeBoundaryError(error, {
|
|
1902
2060
|
code: 'claworld_relay_binding_failed',
|
|
@@ -2059,6 +2217,7 @@ export function createClaworldChannelPlugin({
|
|
|
2059
2217
|
relayClients.delete(accountKey);
|
|
2060
2218
|
}
|
|
2061
2219
|
accountRuntimeContexts.delete(accountKey);
|
|
2220
|
+
accountBindingStates.delete(accountKey);
|
|
2062
2221
|
},
|
|
2063
2222
|
});
|
|
2064
2223
|
|
|
@@ -2200,7 +2359,8 @@ export function createClaworldChannelPlugin({
|
|
|
2200
2359
|
validate: validateClaworldChannelConfig,
|
|
2201
2360
|
listAccountIds: (cfg) => listClaworldAccountIds(cfg),
|
|
2202
2361
|
defaultAccountId: (cfg) => defaultClaworldAccountId(cfg),
|
|
2203
|
-
inspectAccount: (cfg, accountId) =>
|
|
2362
|
+
inspectAccount: (cfg, accountId) =>
|
|
2363
|
+
projectClaworldStatusAccount(inspectClaworldChannelAccount(cfg, accountId)),
|
|
2204
2364
|
resolveAccount: (cfg, accountId) => resolveClaworldChannelAccount(cfg, accountId),
|
|
2205
2365
|
resolveRuntimeConfig: (cfg, accountId) => resolveClaworldRuntimeConfig(cfg, accountId),
|
|
2206
2366
|
isConfigured: (account, cfg) => {
|
|
@@ -2387,6 +2547,15 @@ export function createClaworldChannelPlugin({
|
|
|
2387
2547
|
fetchImpl,
|
|
2388
2548
|
});
|
|
2389
2549
|
},
|
|
2550
|
+
rejectChatRequest: async (context = {}) => {
|
|
2551
|
+
const resolvedContext = await resolveBoundRuntimeContext(context);
|
|
2552
|
+
return rejectChatRequest({
|
|
2553
|
+
runtimeConfig: resolvedContext.runtimeConfig,
|
|
2554
|
+
actorAgentId: resolvedContext.agentId || null,
|
|
2555
|
+
chatRequestId: context.chatRequestId || null,
|
|
2556
|
+
fetchImpl,
|
|
2557
|
+
});
|
|
2558
|
+
},
|
|
2390
2559
|
},
|
|
2391
2560
|
postSetup: {
|
|
2392
2561
|
fetchWorldDirectory: async (context = {}) => {
|
|
@@ -2524,6 +2693,7 @@ export function createClaworldChannelPlugin({
|
|
|
2524
2693
|
mode: context.mode || 'get',
|
|
2525
2694
|
changes: context.changes || null,
|
|
2526
2695
|
enabled: Object.prototype.hasOwnProperty.call(context, 'enabled') ? context.enabled : null,
|
|
2696
|
+
status: context.status || null,
|
|
2527
2697
|
fetchImpl,
|
|
2528
2698
|
logger,
|
|
2529
2699
|
});
|
|
@@ -2712,6 +2882,7 @@ export function createClaworldChannelPlugin({
|
|
|
2712
2882
|
mode: context.mode || 'get',
|
|
2713
2883
|
changes: context.changes || null,
|
|
2714
2884
|
enabled: Object.prototype.hasOwnProperty.call(context, 'enabled') ? context.enabled : null,
|
|
2885
|
+
status: context.status || null,
|
|
2715
2886
|
fetchImpl,
|
|
2716
2887
|
logger,
|
|
2717
2888
|
});
|
|
@@ -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,
|
|
@@ -23,6 +23,8 @@ export const DEFAULT_CLAWORLD_APPROVAL_MODE = DEFAULT_CHAT_REQUEST_APPROVAL_POLI
|
|
|
23
23
|
export const DEFAULT_CLAWORLD_SESSION_TARGET = 'mainagent';
|
|
24
24
|
export const DEFAULT_CLAWORLD_FALLBACK_TARGET = 'mainagent';
|
|
25
25
|
export const CLAWORLD_PLUGIN_TOOL_ALLOW_ENTRY = 'claworld';
|
|
26
|
+
export const MIN_MANAGED_SESSION_VISIBILITY = 'agent';
|
|
27
|
+
export const REQUIRED_SANDBOX_SESSION_TOOLS_VISIBILITY = 'all';
|
|
26
28
|
|
|
27
29
|
export const TOOL_PROFILES = CLAWORLD_TOOL_PROFILES;
|
|
28
30
|
|
|
@@ -128,6 +130,109 @@ function findManagedAccountEntry(config = {}, accountId) {
|
|
|
128
130
|
return {};
|
|
129
131
|
}
|
|
130
132
|
|
|
133
|
+
const SESSION_VISIBILITY_RANK = Object.freeze({
|
|
134
|
+
self: 0,
|
|
135
|
+
tree: 1,
|
|
136
|
+
agent: 2,
|
|
137
|
+
all: 3,
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
const SANDBOX_SESSION_TOOLS_VISIBILITY_RANK = Object.freeze({
|
|
141
|
+
spawned: 0,
|
|
142
|
+
all: 1,
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
function normalizeSessionVisibility(value, fallback = null) {
|
|
146
|
+
const normalized = normalizeText(value, fallback);
|
|
147
|
+
return Object.prototype.hasOwnProperty.call(SESSION_VISIBILITY_RANK, normalized)
|
|
148
|
+
? normalized
|
|
149
|
+
: fallback;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function normalizeSandboxSessionToolsVisibility(value, fallback = null) {
|
|
153
|
+
const normalized = normalizeText(value, fallback);
|
|
154
|
+
return Object.prototype.hasOwnProperty.call(SANDBOX_SESSION_TOOLS_VISIBILITY_RANK, normalized)
|
|
155
|
+
? normalized
|
|
156
|
+
: fallback;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function compareRankedSetting(value, target, rankMap) {
|
|
160
|
+
const nextValue = normalizeText(value, null);
|
|
161
|
+
const nextTarget = normalizeText(target, null);
|
|
162
|
+
const valueRank = Object.prototype.hasOwnProperty.call(rankMap, nextValue) ? rankMap[nextValue] : null;
|
|
163
|
+
const targetRank = Object.prototype.hasOwnProperty.call(rankMap, nextTarget) ? rankMap[nextTarget] : null;
|
|
164
|
+
if (valueRank == null && targetRank == null) return 0;
|
|
165
|
+
if (valueRank == null) return -1;
|
|
166
|
+
if (targetRank == null) return 1;
|
|
167
|
+
return valueRank - targetRank;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export function getEffectiveAgentSandboxMode(config = {}, agentId = DEFAULT_CLAWORLD_AGENT_ID) {
|
|
171
|
+
const normalizedAgentId = normalizeText(agentId, DEFAULT_CLAWORLD_AGENT_ID);
|
|
172
|
+
const agentEntry = findAgentEntry(config, normalizedAgentId);
|
|
173
|
+
const agentSandboxMode = normalizeText(agentEntry?.sandbox?.mode, null);
|
|
174
|
+
if (agentSandboxMode) return agentSandboxMode;
|
|
175
|
+
return normalizeText(config?.agents?.defaults?.sandbox?.mode, 'off');
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export function sandboxModeNeedsSessionToolsVisibility(mode) {
|
|
179
|
+
return mode === 'all' || mode === 'non-main';
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function ensureManagedSessionRoutingVisibility(config = {}, {
|
|
183
|
+
agentId = DEFAULT_CLAWORLD_AGENT_ID,
|
|
184
|
+
summary = [],
|
|
185
|
+
} = {}) {
|
|
186
|
+
config.tools = ensureObject(config.tools);
|
|
187
|
+
const existingSessionTools = ensureObject(config.tools.sessions);
|
|
188
|
+
const existingVisibility = normalizeSessionVisibility(existingSessionTools.visibility, null);
|
|
189
|
+
if (compareRankedSetting(existingVisibility, MIN_MANAGED_SESSION_VISIBILITY, SESSION_VISIBILITY_RANK) < 0) {
|
|
190
|
+
config.tools.sessions = {
|
|
191
|
+
...existingSessionTools,
|
|
192
|
+
visibility: MIN_MANAGED_SESSION_VISIBILITY,
|
|
193
|
+
};
|
|
194
|
+
summary.push(
|
|
195
|
+
existingVisibility
|
|
196
|
+
? `tools.sessions.visibility raised from ${existingVisibility} to ${MIN_MANAGED_SESSION_VISIBILITY}`
|
|
197
|
+
: `tools.sessions.visibility set to ${MIN_MANAGED_SESSION_VISIBILITY}`,
|
|
198
|
+
);
|
|
199
|
+
} else if (Object.keys(existingSessionTools).length > 0) {
|
|
200
|
+
config.tools.sessions = existingSessionTools;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
const effectiveSandboxMode = getEffectiveAgentSandboxMode(config, agentId);
|
|
204
|
+
if (!sandboxModeNeedsSessionToolsVisibility(effectiveSandboxMode)) {
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
config.agents = ensureObject(config.agents);
|
|
209
|
+
config.agents.defaults = ensureObject(config.agents.defaults);
|
|
210
|
+
const existingSandbox = ensureObject(config.agents.defaults.sandbox);
|
|
211
|
+
const existingSessionToolsVisibility = normalizeSandboxSessionToolsVisibility(
|
|
212
|
+
existingSandbox.sessionToolsVisibility,
|
|
213
|
+
null,
|
|
214
|
+
);
|
|
215
|
+
if (
|
|
216
|
+
compareRankedSetting(
|
|
217
|
+
existingSessionToolsVisibility,
|
|
218
|
+
REQUIRED_SANDBOX_SESSION_TOOLS_VISIBILITY,
|
|
219
|
+
SANDBOX_SESSION_TOOLS_VISIBILITY_RANK,
|
|
220
|
+
) < 0
|
|
221
|
+
) {
|
|
222
|
+
config.agents.defaults.sandbox = {
|
|
223
|
+
...existingSandbox,
|
|
224
|
+
sessionToolsVisibility: REQUIRED_SANDBOX_SESSION_TOOLS_VISIBILITY,
|
|
225
|
+
};
|
|
226
|
+
summary.push(
|
|
227
|
+
existingSessionToolsVisibility
|
|
228
|
+
? `agents.defaults.sandbox.sessionToolsVisibility raised from ${existingSessionToolsVisibility} to ${REQUIRED_SANDBOX_SESSION_TOOLS_VISIBILITY}`
|
|
229
|
+
: `agents.defaults.sandbox.sessionToolsVisibility set to ${REQUIRED_SANDBOX_SESSION_TOOLS_VISIBILITY}`,
|
|
230
|
+
);
|
|
231
|
+
} else if (Object.keys(existingSandbox).length > 0) {
|
|
232
|
+
config.agents.defaults.sandbox = existingSandbox;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
131
236
|
function inferExistingAgentId(config = {}, accountId = DEFAULT_CLAWORLD_ACCOUNT_ID) {
|
|
132
237
|
const bindings = Array.isArray(config?.bindings) ? config.bindings : [];
|
|
133
238
|
const bindingMatch = bindings
|
|
@@ -716,6 +821,11 @@ export function applyClaworldManagedRuntimeConfig(inputConfig = {}, options = {}
|
|
|
716
821
|
: `reconciled claworld binding for ${options.accountId}`,
|
|
717
822
|
);
|
|
718
823
|
|
|
824
|
+
ensureManagedSessionRoutingVisibility(config, {
|
|
825
|
+
agentId: options.agentId,
|
|
826
|
+
summary,
|
|
827
|
+
});
|
|
828
|
+
|
|
719
829
|
return {
|
|
720
830
|
config,
|
|
721
831
|
summary,
|