@xfxstudio/claworld 0.1.0

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 (69) hide show
  1. package/README.md +60 -0
  2. package/bin/claworld.mjs +9 -0
  3. package/index.js +51 -0
  4. package/openclaw.plugin.json +470 -0
  5. package/package.json +76 -0
  6. package/setup-entry.js +6 -0
  7. package/src/lib/accepted-chat-kickoff.js +192 -0
  8. package/src/lib/agent-address.js +46 -0
  9. package/src/lib/agent-profile.js +69 -0
  10. package/src/lib/http-auth.js +151 -0
  11. package/src/lib/policy.js +118 -0
  12. package/src/lib/runtime-errors.js +149 -0
  13. package/src/lib/runtime-guidance.js +458 -0
  14. package/src/openclaw/index.js +53 -0
  15. package/src/openclaw/installer/cli.js +349 -0
  16. package/src/openclaw/installer/constants.js +6 -0
  17. package/src/openclaw/installer/core.js +1548 -0
  18. package/src/openclaw/installer/doctor.js +690 -0
  19. package/src/openclaw/installer/workspace-contract.js +403 -0
  20. package/src/openclaw/plugin/account-identity.js +66 -0
  21. package/src/openclaw/plugin/claworld-channel-plugin.js +3118 -0
  22. package/src/openclaw/plugin/config-schema.js +464 -0
  23. package/src/openclaw/plugin/lifecycle.js +114 -0
  24. package/src/openclaw/plugin/managed-config.js +648 -0
  25. package/src/openclaw/plugin/onboarding.js +291 -0
  26. package/src/openclaw/plugin/register.js +961 -0
  27. package/src/openclaw/plugin/relay-client.js +783 -0
  28. package/src/openclaw/plugin/runtime.js +12 -0
  29. package/src/openclaw/protocol/relay-event-protocol.js +31 -0
  30. package/src/openclaw/runtime/canonical-result-builder.js +116 -0
  31. package/src/openclaw/runtime/demo-session-bootstrap.js +37 -0
  32. package/src/openclaw/runtime/feedback-helper.js +145 -0
  33. package/src/openclaw/runtime/inbound-session-router.js +36 -0
  34. package/src/openclaw/runtime/outbound-session-bridge.js +17 -0
  35. package/src/openclaw/runtime/product-shell-helper.js +1712 -0
  36. package/src/openclaw/runtime/runtime-path.js +19 -0
  37. package/src/openclaw/runtime/system-message-orchestrator.js +1 -0
  38. package/src/openclaw/runtime/tool-contracts.js +714 -0
  39. package/src/openclaw/runtime/tool-inventory.js +92 -0
  40. package/src/openclaw/runtime/world-moderation-helper.js +415 -0
  41. package/src/openclaw/runtime/world-session-startup.js +1 -0
  42. package/src/product-shell/catalog/default-world-catalog.js +296 -0
  43. package/src/product-shell/contracts/candidate-feed.js +330 -0
  44. package/src/product-shell/contracts/chat-request-approval-policy.js +98 -0
  45. package/src/product-shell/contracts/world-manifest.js +435 -0
  46. package/src/product-shell/contracts/world-orchestration.js +1024 -0
  47. package/src/product-shell/feedback/feedback-contract.js +13 -0
  48. package/src/product-shell/feedback/feedback-routes.js +98 -0
  49. package/src/product-shell/feedback/feedback-service.js +254 -0
  50. package/src/product-shell/index.js +163 -0
  51. package/src/product-shell/matching/matchmaking-service.js +340 -0
  52. package/src/product-shell/membership/membership-service.js +277 -0
  53. package/src/product-shell/onboarding/onboarding-routes.js +37 -0
  54. package/src/product-shell/onboarding/onboarding-service.js +230 -0
  55. package/src/product-shell/orchestration/session-orchestrator.js +38 -0
  56. package/src/product-shell/results/result-service.js +15 -0
  57. package/src/product-shell/search/search-service.js +359 -0
  58. package/src/product-shell/social/chat-request-approval-policy.js +332 -0
  59. package/src/product-shell/social/chat-request-routes.js +108 -0
  60. package/src/product-shell/social/chat-request-service.js +632 -0
  61. package/src/product-shell/social/friend-routes.js +82 -0
  62. package/src/product-shell/social/friend-service.js +560 -0
  63. package/src/product-shell/social/social-routes.js +21 -0
  64. package/src/product-shell/social/social-service.js +140 -0
  65. package/src/product-shell/worlds/world-admin-service.js +705 -0
  66. package/src/product-shell/worlds/world-authorization.js +135 -0
  67. package/src/product-shell/worlds/world-broadcast-service.js +299 -0
  68. package/src/product-shell/worlds/world-routes.js +410 -0
  69. package/src/product-shell/worlds/world-service.js +89 -0
@@ -0,0 +1,12 @@
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
+ }
@@ -0,0 +1,31 @@
1
+ const MINIMAL_EVENT_TYPES = [
2
+ 'message',
3
+ 'raise_hand',
4
+ 'session_end',
5
+ 'session_result_ready',
6
+ 'system_message',
7
+ 'ack',
8
+ ];
9
+
10
+ export function createRelayEventProtocol() {
11
+ return {
12
+ version: 'claworld.a2a.v0.scaffold',
13
+ requiredEnvelopeFields: ['event_id', 'session_id', 'type', 'payload', 'ts'],
14
+ eventTypes: MINIMAL_EVENT_TYPES,
15
+ describeEvent(event = {}) {
16
+ return {
17
+ ok: MINIMAL_EVENT_TYPES.includes(event.type),
18
+ missing: this.requiredEnvelopeFields.filter((field) => event[field] == null),
19
+ role: event.role || inferRoleFromType(event.type),
20
+ };
21
+ },
22
+ };
23
+ }
24
+
25
+ function inferRoleFromType(type) {
26
+ if (type === 'system_message') return 'system';
27
+ if (type === 'raise_hand') return 'control';
28
+ if (type === 'message') return 'agent';
29
+ if (type === 'session_result_ready') return 'system';
30
+ return 'control';
31
+ }
@@ -0,0 +1,116 @@
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 raisedHands = normalizedSignals.filter((signal) => signal.type === 'raise_hand').length;
38
+ const hardBlock = normalizedSignals.some(
39
+ (signal) => signal.type === 'block' || signal.metadata?.hardBlock === true || signal.risk >= 0.85,
40
+ );
41
+ if (hardBlock) return 'pass';
42
+ if (raisedHands >= 2 && (matchScore == null || matchScore >= 0.45) && risks.length <= 1) {
43
+ return 'continue';
44
+ }
45
+ if (matchScore != null && matchScore >= 0.65 && risks.length === 0) {
46
+ return 'continue';
47
+ }
48
+ if (matchScore != null && matchScore < 0.25) {
49
+ return 'pass';
50
+ }
51
+ return 'review';
52
+ }
53
+
54
+ export function createCanonicalResultBuilder() {
55
+ return {
56
+ schema: {
57
+ match_score: 'number',
58
+ summary: 'string',
59
+ risks: 'string[]',
60
+ recommendation: 'continue|pass|review',
61
+ evidence: 'SessionMessage[]',
62
+ },
63
+ build({ sessionId, intentSignals = [], conversationSignals = [], agentSignals = [] } = {}) {
64
+ const normalizedIntentSignals = intentSignals.map((signal, index) =>
65
+ normalizeSignal(signal, index, 'intent'),
66
+ );
67
+ const normalizedConversationSignals = conversationSignals.map((signal, index) =>
68
+ normalizeSignal(signal, index, 'conversation'),
69
+ );
70
+ const normalizedAgentSignals = agentSignals.map((signal, index) =>
71
+ normalizeSignal(signal, index, 'agent'),
72
+ );
73
+ const normalizedSignals = [
74
+ ...normalizedIntentSignals,
75
+ ...normalizedConversationSignals,
76
+ ...normalizedAgentSignals,
77
+ ];
78
+
79
+ const matchScore = computeWeightedAverage(normalizedSignals);
80
+ const risks = collectRisks(normalizedSignals);
81
+ const recommendation = pickRecommendation({ matchScore, normalizedSignals, risks });
82
+ const evidence = normalizedSignals
83
+ .sort((a, b) => Math.abs(b.score) + b.risk - (Math.abs(a.score) + a.risk))
84
+ .slice(0, 5)
85
+ .map((signal) => ({
86
+ id: signal.id,
87
+ type: signal.type,
88
+ source: signal.source,
89
+ summary: signal.summary,
90
+ score: signal.score,
91
+ risk: signal.risk,
92
+ }));
93
+
94
+ return {
95
+ sessionId,
96
+ match_score: matchScore,
97
+ summary: buildSummary({
98
+ matchScore,
99
+ riskCount: risks.length,
100
+ intentCount: normalizedIntentSignals.length,
101
+ conversationCount: normalizedConversationSignals.length,
102
+ agentCount: normalizedAgentSignals.length,
103
+ }),
104
+ risks,
105
+ recommendation,
106
+ evidence,
107
+ inputs: {
108
+ intentSignalsCount: normalizedIntentSignals.length,
109
+ conversationSignalsCount: normalizedConversationSignals.length,
110
+ agentSignalsCount: normalizedAgentSignals.length,
111
+ },
112
+ status: 'built',
113
+ };
114
+ },
115
+ };
116
+ }
@@ -0,0 +1,37 @@
1
+ export function createDemoSessionBootstrap() {
2
+ return {
3
+ defaults: {
4
+ users: ['demo-a', 'demo-b'],
5
+ world: 'dating-demo-world',
6
+ allowDemoToken: true,
7
+ maxTurns: 6,
8
+ },
9
+ createLaunchPlan({
10
+ sessionGoal = 'run one stable A2A demo loop',
11
+ users,
12
+ world,
13
+ maxTurns,
14
+ openingMessage = 'You are entering demo mode. Try to complete one stable match loop.',
15
+ } = {}) {
16
+ const resolvedUsers = Array.isArray(users) && users.length > 0 ? users : this.defaults.users;
17
+ const resolvedWorld = world || this.defaults.world;
18
+ const resolvedMaxTurns = Number.isFinite(Number(maxTurns)) ? Number(maxTurns) : this.defaults.maxTurns;
19
+ return {
20
+ sessionGoal,
21
+ world: resolvedWorld,
22
+ users: resolvedUsers,
23
+ maxTurns: resolvedMaxTurns,
24
+ openingMessage,
25
+ steps: [
26
+ 'load demo users',
27
+ 'load demo world',
28
+ 'create manual session',
29
+ 'inject opening system message',
30
+ 'wait for dual raise or manual stop',
31
+ 'build canonical result',
32
+ ],
33
+ status: 'planned',
34
+ };
35
+ },
36
+ };
37
+ }
@@ -0,0 +1,145 @@
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
+ sessionId: normalizeText(normalizedContext.sessionId, null),
114
+ roundId: normalizeText(normalizedContext.roundId, null),
115
+ targetAgentId: normalizeText(normalizedContext.targetAgentId, null),
116
+ targetAgentCode: normalizeText(normalizedContext.targetAgentCode, 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
+ defaultToAddress: normalizeText(resolvedRuntimeConfig.relay?.defaultToAddress, 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
+ }
@@ -0,0 +1,36 @@
1
+ import { OPENCLAW_RUNTIME_PATH, createRuntimePathTrace } from './runtime-path.js';
2
+
3
+ function normalizeSessionTarget(value, fallback) {
4
+ const candidate = typeof value === 'string' ? value.trim() : '';
5
+ return candidate || fallback;
6
+ }
7
+
8
+ export function createInboundSessionRouter() {
9
+ return {
10
+ runtimePath: OPENCLAW_RUNTIME_PATH,
11
+ routeInboundEvent(event = {}, options = {}) {
12
+ const target = normalizeSessionTarget(options.sessionTarget, 'subagent');
13
+ const fallbackTarget = normalizeSessionTarget(options.fallbackTarget, 'mainagent');
14
+ const route = options.route && typeof options.route === 'object' ? options.route : null;
15
+ return {
16
+ action: 'route_inbound_event',
17
+ target,
18
+ fallbackTarget,
19
+ sessionId: event.session_id || null,
20
+ eventId: event.event_id || null,
21
+ fromAgentId: event.from_agent_id || null,
22
+ eventType: event.type || null,
23
+ route,
24
+ sessionKey: route?.sessionKey || null,
25
+ accountId: route?.accountId || null,
26
+ preserveFields: ['session_id', 'event_id', 'from_agent_id'],
27
+ trace: createRuntimePathTrace({
28
+ sessionId: event.session_id || null,
29
+ eventId: event.event_id || null,
30
+ direction: 'inbound',
31
+ }),
32
+ status: route?.sessionKey ? 'resolved' : 'unresolved',
33
+ };
34
+ },
35
+ };
36
+ }
@@ -0,0 +1,17 @@
1
+ import { createRuntimePathTrace } from './runtime-path.js';
2
+
3
+ export function createOutboundSessionBridge() {
4
+ return {
5
+ createReplyEnvelope({ sessionId, messageId = null, replyText, source = 'subagent', control = null } = {}) {
6
+ return {
7
+ session_id: sessionId,
8
+ message_id: messageId,
9
+ reply_text: replyText,
10
+ source,
11
+ control: control && typeof control === 'object' && !Array.isArray(control) ? { ...control } : null,
12
+ trace: createRuntimePathTrace({ sessionId, eventId: messageId, direction: 'outbound' }),
13
+ status: 'scaffold_only',
14
+ };
15
+ },
16
+ };
17
+ }