@xfxstudio/claworld 2026.4.14-testing.2 → 2026.4.16-testing.1

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.
Files changed (47) hide show
  1. package/openclaw.plugin.json +1 -1
  2. package/package.json +1 -1
  3. package/index.js +0 -50
  4. package/setup-entry.js +0 -6
  5. package/skills/claworld-a2a-channel-agent/SKILL.md +0 -218
  6. package/skills/claworld-help/SKILL.md +0 -304
  7. package/skills/claworld-join-and-chat/SKILL.md +0 -526
  8. package/skills/claworld-manage-worlds/SKILL.md +0 -292
  9. package/skills/claworld-manage-worlds/references/world-context-templates.md +0 -145
  10. package/src/lib/chat-request.js +0 -366
  11. package/src/lib/public-identity.js +0 -175
  12. package/src/lib/relay/agent-readable-markdown.js +0 -385
  13. package/src/lib/relay/kickoff-progress.js +0 -162
  14. package/src/lib/relay/kickoff-text.js +0 -191
  15. package/src/lib/relay/shared.js +0 -30
  16. package/src/lib/runtime-errors.js +0 -149
  17. package/src/openclaw/index.js +0 -51
  18. package/src/openclaw/plugin/account-identity.js +0 -73
  19. package/src/openclaw/plugin/claworld-channel-plugin.js +0 -3483
  20. package/src/openclaw/plugin/config-schema.js +0 -392
  21. package/src/openclaw/plugin/lifecycle.js +0 -114
  22. package/src/openclaw/plugin/managed-config.js +0 -1054
  23. package/src/openclaw/plugin/onboarding.js +0 -312
  24. package/src/openclaw/plugin/register-tooling.js +0 -728
  25. package/src/openclaw/plugin/register.js +0 -1616
  26. package/src/openclaw/plugin/relay-client-shared.js +0 -146
  27. package/src/openclaw/plugin/relay-client.js +0 -1469
  28. package/src/openclaw/plugin/runtime-backup.js +0 -105
  29. package/src/openclaw/plugin/runtime.js +0 -12
  30. package/src/openclaw/plugin-version.js +0 -67
  31. package/src/openclaw/protocol/relay-event-protocol.js +0 -43
  32. package/src/openclaw/runtime/backend-error-context.js +0 -91
  33. package/src/openclaw/runtime/canonical-result-builder.js +0 -126
  34. package/src/openclaw/runtime/demo-session-bootstrap.js +0 -32
  35. package/src/openclaw/runtime/feedback-helper.js +0 -145
  36. package/src/openclaw/runtime/inbound-session-router.js +0 -44
  37. package/src/openclaw/runtime/outbound-session-bridge.js +0 -29
  38. package/src/openclaw/runtime/product-shell-helper.js +0 -931
  39. package/src/openclaw/runtime/runtime-path.js +0 -19
  40. package/src/openclaw/runtime/system-message-orchestrator.js +0 -1
  41. package/src/openclaw/runtime/tool-contracts.js +0 -939
  42. package/src/openclaw/runtime/tool-inventory.js +0 -83
  43. package/src/openclaw/runtime/world-membership-helper.js +0 -320
  44. package/src/openclaw/runtime/world-moderation-helper.js +0 -508
  45. package/src/product-shell/contracts/chat-request-approval-policy.js +0 -93
  46. package/src/product-shell/contracts/world-orchestration.js +0 -734
  47. package/src/product-shell/orchestration/world-conversation-text.js +0 -229
@@ -1,105 +0,0 @@
1
- import fs from 'node:fs/promises';
2
- import os from 'node:os';
3
- import path from 'node:path';
4
-
5
- import {
6
- DEFAULT_CLAWORLD_ACCOUNT_ID,
7
- findClaworldManagedRuntimeBackup,
8
- setClaworldManagedRuntimeBackupState,
9
- stripClaworldManagedRuntimeConfig,
10
- } from './managed-config.js';
11
-
12
- function normalizeText(value, fallback = null) {
13
- if (value == null) return fallback;
14
- const normalized = String(value).trim();
15
- return normalized || fallback;
16
- }
17
-
18
- export function resolveDefaultOpenClawConfigPath() {
19
- return path.resolve(normalizeText(
20
- process.env.OPENCLAW_CONFIG_PATH,
21
- path.join(os.homedir(), '.openclaw', 'openclaw.json'),
22
- ));
23
- }
24
-
25
- export function resolveClaworldInstallerStatePath(configPath = resolveDefaultOpenClawConfigPath()) {
26
- const resolvedConfigPath = path.resolve(String(configPath));
27
- return path.join(path.dirname(resolvedConfigPath), '.claworld-installer-state.json');
28
- }
29
-
30
- async function loadInstallerStateFromDisk(installerStatePath) {
31
- try {
32
- const raw = await fs.readFile(installerStatePath, 'utf8');
33
- const parsed = JSON.parse(raw);
34
- return parsed && typeof parsed === 'object' && !Array.isArray(parsed) ? parsed : {};
35
- } catch (error) {
36
- if (error?.code === 'ENOENT') return {};
37
- throw error;
38
- }
39
- }
40
-
41
- async function writeInstallerStateToDisk(installerStatePath, installerState) {
42
- const nextState = installerState && typeof installerState === 'object' && !Array.isArray(installerState)
43
- ? installerState
44
- : {};
45
- if (Object.keys(nextState).length === 0) {
46
- await fs.rm(installerStatePath, { force: true });
47
- return;
48
- }
49
- await fs.mkdir(path.dirname(installerStatePath), { recursive: true });
50
- await fs.writeFile(installerStatePath, `${JSON.stringify(nextState, null, 2)}\n`, 'utf8');
51
- }
52
-
53
- export async function loadClaworldRuntimeBackup({
54
- accountId = DEFAULT_CLAWORLD_ACCOUNT_ID,
55
- configPath = resolveDefaultOpenClawConfigPath(),
56
- } = {}) {
57
- const installerStatePath = resolveClaworldInstallerStatePath(configPath);
58
- const installerState = await loadInstallerStateFromDisk(installerStatePath);
59
- return {
60
- installerStatePath,
61
- installerState,
62
- backup: findClaworldManagedRuntimeBackup(installerState, accountId),
63
- };
64
- }
65
-
66
- export async function persistClaworldRuntimeBackup({
67
- runtime = null,
68
- accountId = DEFAULT_CLAWORLD_ACCOUNT_ID,
69
- configPath = resolveDefaultOpenClawConfigPath(),
70
- } = {}) {
71
- if (!runtime?.config?.loadConfig) {
72
- return { skipped: true, reason: 'missing_runtime_config_loader' };
73
- }
74
-
75
- const currentConfig = await runtime.config.loadConfig();
76
- const { backup } = stripClaworldManagedRuntimeConfig(currentConfig, {
77
- accountId,
78
- preserveBackup: true,
79
- });
80
-
81
- if (!backup?.appToken) {
82
- return { skipped: true, reason: 'missing_app_token', backup: null };
83
- }
84
-
85
- const installerStatePath = resolveClaworldInstallerStatePath(configPath);
86
- const installerState = await loadInstallerStateFromDisk(installerStatePath);
87
- const previousBackup = findClaworldManagedRuntimeBackup(installerState, accountId);
88
- if (JSON.stringify(previousBackup || null) === JSON.stringify(backup)) {
89
- return {
90
- skipped: true,
91
- reason: 'already_persisted',
92
- installerStatePath,
93
- backup,
94
- };
95
- }
96
-
97
- setClaworldManagedRuntimeBackupState(installerState, accountId, backup);
98
- await writeInstallerStateToDisk(installerStatePath, installerState);
99
- return {
100
- skipped: false,
101
- ok: true,
102
- installerStatePath,
103
- backup,
104
- };
105
- }
@@ -1,12 +0,0 @@
1
- let currentRuntime = null;
2
-
3
- export function setClaworldRuntime(runtime) {
4
- currentRuntime = runtime || null;
5
- }
6
-
7
- export function getClaworldRuntime() {
8
- if (!currentRuntime) {
9
- throw new Error('Claworld runtime not initialized');
10
- }
11
- return currentRuntime;
12
- }
@@ -1,67 +0,0 @@
1
- import { readFileSync } from 'node:fs';
2
- import repoPackageJson from '../../package.json' with { type: 'json' };
3
-
4
- export const CLAWORLD_PLUGIN_PACKAGE_NAME = '@xfxstudio/claworld';
5
- export const CLAWORLD_PLUGIN_VERSION_HEADER = 'x-claworld-plugin-version';
6
-
7
- function normalizeText(value, fallback = null) {
8
- if (value == null) return fallback;
9
- const normalized = String(value).trim();
10
- return normalized || fallback;
11
- }
12
-
13
- function normalizeHeaderValue(value) {
14
- if (Array.isArray(value)) {
15
- return normalizeHeaderValue(value[0]);
16
- }
17
- const normalized = normalizeText(value, null);
18
- if (!normalized) return null;
19
- return normalized.split(',')[0]?.trim() || null;
20
- }
21
-
22
- export function normalizeClaworldPluginVersion(value, fallback = null) {
23
- const normalized = normalizeText(value, null);
24
- if (!normalized) return fallback;
25
- const withoutPrefix = normalized.replace(/^v/i, '');
26
- if (!/^\d+(?:\.\d+)*(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/.test(withoutPrefix)) {
27
- return fallback;
28
- }
29
- return withoutPrefix;
30
- }
31
-
32
- function resolveCurrentPluginVersion() {
33
- const repoVersion = normalizeClaworldPluginVersion(repoPackageJson?.version, null);
34
- if (repoPackageJson?.name === CLAWORLD_PLUGIN_PACKAGE_NAME && repoVersion) {
35
- return repoVersion;
36
- }
37
-
38
- try {
39
- const publishSurfaceSource = readFileSync(
40
- new URL('../../packages/openclaw-plugin/package.json', import.meta.url),
41
- 'utf8',
42
- );
43
- const publishSurfacePackageJson = JSON.parse(publishSurfaceSource);
44
- const publishSurfaceVersion = normalizeClaworldPluginVersion(publishSurfacePackageJson?.version, null);
45
- if (publishSurfacePackageJson?.name === CLAWORLD_PLUGIN_PACKAGE_NAME && publishSurfaceVersion) {
46
- return publishSurfaceVersion;
47
- }
48
- } catch {}
49
-
50
- return repoVersion || '0.0.0';
51
- }
52
-
53
- export const CLAWORLD_PLUGIN_CURRENT_VERSION = resolveCurrentPluginVersion();
54
-
55
- export function readClaworldPluginVersionFromHeaders(headers = {}) {
56
- const rawVersion = normalizeHeaderValue(headers?.[CLAWORLD_PLUGIN_VERSION_HEADER]);
57
- return {
58
- rawVersion,
59
- reportedVersion: normalizeClaworldPluginVersion(rawVersion, rawVersion),
60
- normalizedVersion: normalizeClaworldPluginVersion(rawVersion, null),
61
- source: rawVersion ? CLAWORLD_PLUGIN_VERSION_HEADER : null,
62
- };
63
- }
64
-
65
- export function buildClaworldRelayClientVersion(version = CLAWORLD_PLUGIN_CURRENT_VERSION) {
66
- return `claworld-plugin/${normalizeClaworldPluginVersion(version, CLAWORLD_PLUGIN_CURRENT_VERSION)}`;
67
- }
@@ -1,43 +0,0 @@
1
- export const CLAWORLD_PLUGIN_BRIDGE_PROTOCOL = 'claworld.delivery_reply.v1';
2
-
3
- const DELIVERY_EVENT_TYPE = 'delivery';
4
-
5
- function normalizeText(value, fallback = null) {
6
- if (value == null) return fallback;
7
- const normalized = String(value).trim();
8
- return normalized || fallback;
9
- }
10
-
11
- function normalizePayload(payload = null) {
12
- if (!payload || typeof payload !== 'object' || Array.isArray(payload)) return {};
13
- return { ...payload };
14
- }
15
-
16
- export function createRelayEventProtocol() {
17
- return {
18
- version: CLAWORLD_PLUGIN_BRIDGE_PROTOCOL,
19
- eventTypes: [DELIVERY_EVENT_TYPE],
20
- requiredEnvelopeFields: ['eventType', 'deliveryId', 'sessionKey', 'payload'],
21
- describeEvent(event = {}) {
22
- const payload = normalizePayload(event.payload);
23
- const missing = [];
24
- if (normalizeText(event.eventType, null) !== DELIVERY_EVENT_TYPE) {
25
- missing.push('eventType');
26
- }
27
- if (!normalizeText(event.deliveryId, null)) {
28
- missing.push('deliveryId');
29
- }
30
- if (!normalizeText(event.sessionKey, null)) {
31
- missing.push('sessionKey');
32
- }
33
- if (!normalizeText(payload.text, null)) {
34
- missing.push('payload.text');
35
- }
36
- return {
37
- ok: missing.length === 0,
38
- missing,
39
- role: 'delivery',
40
- };
41
- },
42
- };
43
- }
@@ -1,91 +0,0 @@
1
- function normalizeText(value, fallback = null) {
2
- if (value == null) return fallback;
3
- const normalized = String(value).trim();
4
- return normalized || fallback;
5
- }
6
-
7
- function normalizeFieldError(fieldError = {}) {
8
- const fieldId = normalizeText(fieldError.fieldId, null);
9
- const message = normalizeText(fieldError.message, null);
10
- const code = normalizeText(fieldError.code, null);
11
- if (!fieldId && !message && !code) return null;
12
- return {
13
- ...(fieldId ? { fieldId } : {}),
14
- ...(message ? { message } : {}),
15
- ...(code ? { code } : {}),
16
- };
17
- }
18
-
19
- function normalizeMissingField(field = {}) {
20
- const fieldId = normalizeText(field.fieldId, null);
21
- const label = normalizeText(field.label, null);
22
- const description = normalizeText(field.description, null);
23
- const message = normalizeText(field.message, null);
24
- const code = normalizeText(field.code, null);
25
- if (!fieldId && !label && !description && !message && !code) return null;
26
- return {
27
- ...(fieldId ? { fieldId } : {}),
28
- ...(label ? { label } : {}),
29
- ...(description ? { description } : {}),
30
- ...(message ? { message } : {}),
31
- ...(code ? { code } : {}),
32
- };
33
- }
34
-
35
- function normalizePublicIdentity(publicIdentity = {}) {
36
- const status = normalizeText(publicIdentity.status, null);
37
- const displayName = normalizeText(publicIdentity.displayName, null);
38
- const code = normalizeText(publicIdentity.code, null);
39
- const displayIdentity = normalizeText(publicIdentity.displayIdentity, null);
40
- const confirmedAt = normalizeText(publicIdentity.confirmedAt, null);
41
- const updatedAt = normalizeText(publicIdentity.updatedAt, null);
42
- if (!status && !displayName && !code && !displayIdentity && !confirmedAt && !updatedAt) return null;
43
- return {
44
- ...(status ? { status } : {}),
45
- ...(displayName ? { displayName } : {}),
46
- ...(code ? { code } : {}),
47
- ...(displayIdentity ? { displayIdentity } : {}),
48
- ...(confirmedAt ? { confirmedAt } : {}),
49
- ...(updatedAt ? { updatedAt } : {}),
50
- };
51
- }
52
-
53
- export function normalizeBackendFieldError(fieldError = {}) {
54
- return normalizeFieldError(fieldError);
55
- }
56
-
57
- export function normalizeBackendMissingField(field = {}) {
58
- return normalizeMissingField(field);
59
- }
60
-
61
- export function normalizeBackendPublicIdentity(publicIdentity = {}) {
62
- return normalizePublicIdentity(publicIdentity);
63
- }
64
-
65
- export function extractBackendErrorContext(payload = {}) {
66
- if (!payload || typeof payload !== 'object' || Array.isArray(payload)) return {};
67
-
68
- const backendCode = normalizeText(payload.error, null);
69
- const backendMessage = normalizeText(payload.message, null);
70
- const requiredAction = normalizeText(payload.requiredAction, null);
71
- const nextAction = normalizeText(payload.nextAction, null);
72
- const nextTool = normalizeText(payload.nextTool, null);
73
- const fieldErrors = Array.isArray(payload.fieldErrors)
74
- ? payload.fieldErrors.map((fieldError) => normalizeFieldError(fieldError)).filter(Boolean)
75
- : [];
76
- const missingFields = Array.isArray(payload.missingFields)
77
- ? payload.missingFields.map((field) => normalizeMissingField(field)).filter(Boolean)
78
- : [];
79
- const publicIdentity = normalizePublicIdentity(payload.publicIdentity);
80
-
81
- return {
82
- ...(backendCode ? { backendCode } : {}),
83
- ...(backendMessage ? { backendMessage } : {}),
84
- ...(requiredAction ? { requiredAction } : {}),
85
- ...(nextAction ? { nextAction } : {}),
86
- ...(nextTool ? { nextTool } : {}),
87
- ...(fieldErrors.length > 0 ? { fieldErrors } : {}),
88
- ...(missingFields.length > 0 ? { missingFields } : {}),
89
- ...(publicIdentity ? { publicIdentity } : {}),
90
- };
91
- }
@@ -1,126 +0,0 @@
1
- function normalizeSignal(signal = {}, index = 0, source = 'unknown') {
2
- const score = Number.isFinite(Number(signal.score)) ? Number(signal.score) : 0;
3
- const risk = Number.isFinite(Number(signal.risk)) ? Number(signal.risk) : 0;
4
- return {
5
- id: signal.id || `${source}_${index + 1}`,
6
- type: signal.type || 'note',
7
- source,
8
- score,
9
- risk,
10
- summary: signal.summary || signal.text || signal.type || `${source} signal ${index + 1}`,
11
- weight: Number.isFinite(Number(signal.weight)) ? Number(signal.weight) : 1,
12
- metadata: signal.metadata || {},
13
- };
14
- }
15
-
16
- function computeWeightedAverage(signals) {
17
- if (signals.length === 0) return null;
18
- const totalWeight = signals.reduce((sum, signal) => sum + signal.weight, 0);
19
- if (totalWeight <= 0) return null;
20
- const totalScore = signals.reduce((sum, signal) => sum + signal.score * signal.weight, 0);
21
- return Number((totalScore / totalWeight).toFixed(3));
22
- }
23
-
24
- function collectRisks(signals) {
25
- return signals
26
- .filter((signal) => signal.risk > 0)
27
- .sort((a, b) => b.risk - a.risk)
28
- .map((signal) => signal.summary);
29
- }
30
-
31
- function buildSummary({ matchScore, riskCount, intentCount, conversationCount, agentCount }) {
32
- const scoreText = matchScore == null ? 'insufficient evidence' : `match score ${matchScore}`;
33
- return `Canonical result built from ${intentCount} intent, ${conversationCount} conversation, ${agentCount} agent signals; ${scoreText}; ${riskCount} risk signal(s).`;
34
- }
35
-
36
- function pickRecommendation({ matchScore, normalizedSignals, risks }) {
37
- const readySignals = normalizedSignals.filter(
38
- (signal) => signal.type === 'next_step_ready'
39
- || signal.type === 'human_handoff_ready'
40
- || signal.type === 'conversation_complete',
41
- ).length;
42
- const hardBlock = normalizedSignals.some(
43
- (signal) => signal.type === 'block' || signal.metadata?.hardBlock === true || signal.risk >= 0.85,
44
- );
45
- if (hardBlock) return 'pass';
46
- if (readySignals >= 2 && (matchScore == null || matchScore >= 0.45) && risks.length <= 1) {
47
- return 'continue';
48
- }
49
- if (matchScore != null && matchScore >= 0.65 && risks.length === 0) {
50
- return 'continue';
51
- }
52
- if (matchScore != null && matchScore < 0.25) {
53
- return 'pass';
54
- }
55
- return 'review';
56
- }
57
-
58
- export function createCanonicalResultBuilder() {
59
- return {
60
- schema: {
61
- match_score: 'number',
62
- summary: 'string',
63
- risks: 'string[]',
64
- recommendation: 'continue|pass|review',
65
- evidence: 'ConversationMessage[]',
66
- },
67
- build({
68
- conversationId = null,
69
- intentSignals = [],
70
- conversationSignals = [],
71
- agentSignals = [],
72
- } = {}) {
73
- const resolvedConversationId = conversationId || null;
74
- const normalizedIntentSignals = intentSignals.map((signal, index) =>
75
- normalizeSignal(signal, index, 'intent'),
76
- );
77
- const normalizedConversationSignals = conversationSignals.map((signal, index) =>
78
- normalizeSignal(signal, index, 'conversation'),
79
- );
80
- const normalizedAgentSignals = agentSignals.map((signal, index) =>
81
- normalizeSignal(signal, index, 'agent'),
82
- );
83
- const normalizedSignals = [
84
- ...normalizedIntentSignals,
85
- ...normalizedConversationSignals,
86
- ...normalizedAgentSignals,
87
- ];
88
-
89
- const matchScore = computeWeightedAverage(normalizedSignals);
90
- const risks = collectRisks(normalizedSignals);
91
- const recommendation = pickRecommendation({ matchScore, normalizedSignals, risks });
92
- const evidence = normalizedSignals
93
- .sort((a, b) => Math.abs(b.score) + b.risk - (Math.abs(a.score) + a.risk))
94
- .slice(0, 5)
95
- .map((signal) => ({
96
- id: signal.id,
97
- type: signal.type,
98
- source: signal.source,
99
- summary: signal.summary,
100
- score: signal.score,
101
- risk: signal.risk,
102
- }));
103
-
104
- return {
105
- conversationId: resolvedConversationId,
106
- match_score: matchScore,
107
- summary: buildSummary({
108
- matchScore,
109
- riskCount: risks.length,
110
- intentCount: normalizedIntentSignals.length,
111
- conversationCount: normalizedConversationSignals.length,
112
- agentCount: normalizedAgentSignals.length,
113
- }),
114
- risks,
115
- recommendation,
116
- evidence,
117
- inputs: {
118
- intentSignalsCount: normalizedIntentSignals.length,
119
- conversationSignalsCount: normalizedConversationSignals.length,
120
- agentSignalsCount: normalizedAgentSignals.length,
121
- },
122
- status: 'built',
123
- };
124
- },
125
- };
126
- }
@@ -1,32 +0,0 @@
1
- export function createDemoSessionBootstrap() {
2
- return {
3
- defaults: {
4
- users: ['demo-a', 'demo-b'],
5
- world: 'dating-demo-world',
6
- allowDemoToken: true,
7
- },
8
- createLaunchPlan({
9
- sessionGoal = 'run one stable A2A demo loop',
10
- users,
11
- world,
12
- openingMessage = 'You are entering demo mode. Try to complete one stable match loop.',
13
- } = {}) {
14
- const resolvedUsers = Array.isArray(users) && users.length > 0 ? users : this.defaults.users;
15
- const resolvedWorld = world || this.defaults.world;
16
- return {
17
- sessionGoal,
18
- world: resolvedWorld,
19
- users: resolvedUsers,
20
- openingMessage,
21
- steps: [
22
- 'load demo users',
23
- 'load demo world',
24
- 'create manual session',
25
- 'inject opening system message',
26
- 'wait for conversation closure or manual stop',
27
- ],
28
- status: 'planned',
29
- };
30
- },
31
- };
32
- }
@@ -1,145 +0,0 @@
1
- import { resolveClaworldRuntimeConfig } from '../plugin/config-schema.js';
2
- import { buildRuntimeAuthHeaders } from '../plugin/account-identity.js';
3
- import { createRuntimeBoundaryError } from '../../lib/runtime-errors.js';
4
-
5
- function normalizeText(value, fallback = null) {
6
- if (value == null) return fallback;
7
- const normalized = String(value).trim();
8
- return normalized || fallback;
9
- }
10
-
11
- function normalizeStringList(values = []) {
12
- if (!Array.isArray(values)) return [];
13
- return [...new Set(values.map((value) => normalizeText(value, null)).filter(Boolean))];
14
- }
15
-
16
- function normalizeObject(value) {
17
- if (!value || typeof value !== 'object' || Array.isArray(value)) return {};
18
- return value;
19
- }
20
-
21
- async function fetchJson(fetchImpl, url, init = {}) {
22
- let response;
23
- try {
24
- response = await fetchImpl(url, init);
25
- } catch (error) {
26
- throw createRuntimeBoundaryError({
27
- code: 'relay_fetch_failed',
28
- category: 'transport',
29
- status: 502,
30
- message: `fetch failed: ${error?.message || String(error)}`,
31
- publicMessage: 'relay fetch failed',
32
- recoverable: true,
33
- context: {
34
- fetchUrl: url,
35
- fetchMethod: init?.method || 'GET',
36
- },
37
- cause: error,
38
- });
39
- }
40
-
41
- let body = null;
42
- try {
43
- body = await response.json();
44
- } catch {
45
- body = null;
46
- }
47
-
48
- return { ok: response.ok, status: response.status, body };
49
- }
50
-
51
- function normalizeRelayHttpBaseUrl(serverUrl) {
52
- const parsed = new URL(serverUrl);
53
- if (parsed.protocol === 'ws:') parsed.protocol = 'http:';
54
- if (parsed.protocol === 'wss:') parsed.protocol = 'https:';
55
- parsed.pathname = '';
56
- parsed.search = '';
57
- parsed.hash = '';
58
- return parsed.toString().replace(/\/$/, '');
59
- }
60
-
61
- export async function submitFeedbackReport({
62
- cfg = {},
63
- accountId = null,
64
- runtimeConfig = null,
65
- agentId = null,
66
- category = null,
67
- title = null,
68
- goal = null,
69
- actualBehavior = null,
70
- expectedBehavior = null,
71
- impact = null,
72
- details = null,
73
- reproductionSteps = [],
74
- context = {},
75
- fetchImpl,
76
- logger = console,
77
- toolCallId = null,
78
- pluginVersion = null,
79
- toolContractVersion = null,
80
- } = {}) {
81
- if (typeof fetchImpl !== 'function') {
82
- throw new Error('fetch is unavailable for claworld feedback helper');
83
- }
84
-
85
- const resolvedAgentId = normalizeText(agentId, null);
86
- if (!resolvedAgentId) {
87
- throw new Error('claworld feedback helper requires agentId');
88
- }
89
-
90
- const normalizedContext = normalizeObject(context);
91
- const resolvedRuntimeConfig = runtimeConfig || resolveClaworldRuntimeConfig(cfg, accountId);
92
- const baseUrl = normalizeRelayHttpBaseUrl(resolvedRuntimeConfig.serverUrl);
93
- const result = await fetchJson(fetchImpl, `${baseUrl}/v1/feedback`, {
94
- method: 'POST',
95
- headers: buildRuntimeAuthHeaders(resolvedRuntimeConfig, {
96
- accept: 'application/json',
97
- 'content-type': 'application/json',
98
- ...(resolvedRuntimeConfig.apiKey ? { 'x-api-key': resolvedRuntimeConfig.apiKey } : {}),
99
- }),
100
- body: JSON.stringify({
101
- agentId: resolvedAgentId,
102
- accountId: normalizeText(resolvedRuntimeConfig.accountId, normalizeText(accountId, null)),
103
- category,
104
- title,
105
- goal,
106
- actualBehavior,
107
- expectedBehavior,
108
- impact,
109
- details,
110
- reproductionSteps,
111
- context: {
112
- worldId: normalizeText(normalizedContext.worldId, null),
113
- conversationKey: normalizeText(normalizedContext.conversationKey, null),
114
- turnId: normalizeText(normalizedContext.turnId, null),
115
- deliveryId: normalizeText(normalizedContext.deliveryId, null),
116
- targetAgentId: normalizeText(normalizedContext.targetAgentId, null),
117
- tags: normalizeStringList(normalizedContext.tags),
118
- metadata: normalizeObject(normalizedContext.metadata),
119
- },
120
- source: 'openclaw_tool',
121
- runtimeContext: {
122
- channelId: 'claworld',
123
- toolName: 'claworld_submit_feedback',
124
- toolCallId: normalizeText(toolCallId, null),
125
- pluginVersion: normalizeText(pluginVersion, null),
126
- toolContractVersion: normalizeText(toolContractVersion, null),
127
- accountId: normalizeText(resolvedRuntimeConfig.accountId, normalizeText(accountId, null)),
128
- serverUrl: baseUrl,
129
- relayAgentId: resolvedAgentId,
130
- defaultTargetAgentId: normalizeText(resolvedRuntimeConfig.relay?.defaultTargetAgentId, null),
131
- },
132
- }),
133
- });
134
-
135
- if (!result.ok) {
136
- logger.error?.('[claworld:feedback] feedback submit failed', {
137
- status: result.status,
138
- accountId: resolvedRuntimeConfig.accountId || accountId || null,
139
- body: result.body,
140
- });
141
- throw new Error(`claworld feedback submit failed: ${result.status}`);
142
- }
143
-
144
- return result.body;
145
- }
@@ -1,44 +0,0 @@
1
- import { OPENCLAW_RUNTIME_PATH, createRuntimePathTrace } from './runtime-path.js';
2
-
3
- function normalizeText(value, fallback = null) {
4
- if (value == null) return fallback;
5
- const normalized = String(value).trim();
6
- return normalized || fallback;
7
- }
8
-
9
- function normalizePayload(payload = null) {
10
- if (!payload || typeof payload !== 'object' || Array.isArray(payload)) return {};
11
- return { ...payload };
12
- }
13
-
14
- export function createInboundSessionRouter() {
15
- return {
16
- runtimePath: OPENCLAW_RUNTIME_PATH,
17
- routeInboundEvent(event = {}, options = {}) {
18
- const eventType = normalizeText(event.eventType || event.type, null);
19
- const deliveryId = normalizeText(event.deliveryId || event.event_id || event.eventId, null);
20
- const sessionKey = normalizeText(event.sessionKey, null);
21
- const payload = normalizePayload(event.payload);
22
- return {
23
- action: 'route_delivery',
24
- target: normalizeText(options.sessionTarget, 'mainagent'),
25
- fallbackTarget: normalizeText(options.fallbackTarget, 'mainagent'),
26
- eventType,
27
- deliveryId,
28
- sessionKey,
29
- payload,
30
- metadata: event.metadata && typeof event.metadata === 'object' && !Array.isArray(event.metadata)
31
- ? { ...event.metadata }
32
- : {},
33
- trace: createRuntimePathTrace({
34
- sessionKey,
35
- eventId: deliveryId,
36
- direction: 'inbound',
37
- }),
38
- status: eventType === 'delivery' && deliveryId && sessionKey && normalizeText(payload.text, null)
39
- ? 'resolved'
40
- : 'invalid',
41
- };
42
- },
43
- };
44
- }