@xfxstudio/claworld 0.1.5 → 0.2.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.
- package/README.md +12 -29
- package/openclaw.plugin.json +5 -29
- package/package.json +4 -12
- package/skills/claworld-help/SKILL.md +50 -182
- package/skills/claworld-join-and-chat/SKILL.md +78 -288
- package/skills/claworld-manage-worlds/SKILL.md +71 -288
- package/src/lib/chat-request.js +347 -0
- package/src/lib/{accepted-chat-kickoff.js → relay/kickoff-text.js} +67 -26
- package/src/openclaw/index.js +0 -5
- package/src/openclaw/installer/cli.js +18 -9
- package/src/openclaw/installer/core.js +12 -6
- package/src/openclaw/installer/doctor.js +69 -31
- package/src/openclaw/installer/workspace-contract.js +33 -9
- package/src/openclaw/plugin/claworld-channel-plugin.js +118 -623
- package/src/openclaw/plugin/config-schema.js +3 -15
- package/src/openclaw/plugin/managed-config.js +98 -47
- package/src/openclaw/plugin/onboarding.js +7 -3
- package/src/openclaw/plugin/register.js +37 -336
- package/src/openclaw/plugin/relay-client.js +111 -101
- package/src/openclaw/protocol/relay-event-protocol.js +34 -22
- package/src/openclaw/runtime/canonical-result-builder.js +15 -5
- package/src/openclaw/runtime/demo-session-bootstrap.js +0 -4
- package/src/openclaw/runtime/feedback-helper.js +3 -2
- package/src/openclaw/runtime/inbound-session-router.js +28 -20
- package/src/openclaw/runtime/outbound-session-bridge.js +21 -9
- package/src/openclaw/runtime/product-shell-helper.js +43 -636
- package/src/openclaw/runtime/runtime-path.js +2 -2
- package/src/openclaw/runtime/system-message-orchestrator.js +1 -1
- package/src/openclaw/runtime/tool-contracts.js +33 -258
- package/src/openclaw/runtime/world-moderation-helper.js +11 -65
- package/src/product-shell/catalog/default-world-catalog.js +9 -27
- package/src/product-shell/contracts/candidate-feed.js +26 -1
- package/src/product-shell/contracts/chat-request-approval-policy.js +4 -4
- package/src/product-shell/contracts/world-manifest.js +115 -160
- package/src/product-shell/contracts/world-orchestration.js +47 -322
- package/src/product-shell/feedback/feedback-routes.js +4 -3
- package/src/product-shell/feedback/feedback-service.js +11 -8
- package/src/product-shell/index.js +5 -6
- package/src/product-shell/membership/membership-service.js +125 -147
- package/src/product-shell/onboarding/onboarding-service.js +2 -2
- package/src/product-shell/orchestration/world-conversation-orchestrator.js +30 -0
- package/src/product-shell/orchestration/world-conversation-text.js +231 -0
- package/src/product-shell/results/result-service.js +9 -3
- package/src/product-shell/search/search-service.js +28 -1
- package/src/product-shell/social/chat-request-routes.js +0 -1
- package/src/product-shell/social/chat-request-service.js +1 -102
- package/src/product-shell/worlds/world-admin-service.js +85 -276
- package/src/product-shell/worlds/world-authorization.js +3 -5
- package/src/product-shell/worlds/world-routes.js +8 -38
- package/src/product-shell/worlds/world-service.js +3 -3
- package/src/product-shell/worlds/world-text.js +77 -0
- package/src/lib/runtime-guidance.js +0 -457
- package/src/openclaw/runtime/world-session-startup.js +0 -1
- package/src/product-shell/orchestration/session-orchestrator.js +0 -38
|
@@ -1,21 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
buildWorldProfileCollectionFlow,
|
|
4
|
-
} from '../contracts/world-orchestration.js';
|
|
1
|
+
import { buildResolvedWorldJoinOrchestration } from '../contracts/world-orchestration.js';
|
|
2
|
+
import { buildParticipantContextText } from '../worlds/world-text.js';
|
|
5
3
|
|
|
6
|
-
function
|
|
7
|
-
if (value == null) return
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
return false;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
function projectMissingField(field = {}) {
|
|
14
|
-
return {
|
|
15
|
-
fieldId: field.fieldId,
|
|
16
|
-
label: field.label,
|
|
17
|
-
description: field.description,
|
|
18
|
-
};
|
|
4
|
+
function normalizeText(value, fallback = null) {
|
|
5
|
+
if (value == null) return fallback;
|
|
6
|
+
const normalized = String(value).trim();
|
|
7
|
+
return normalized || fallback;
|
|
19
8
|
}
|
|
20
9
|
|
|
21
10
|
function createConfigurationError() {
|
|
@@ -49,67 +38,30 @@ function createInvalidJoinRequestError(fieldId, message = `${fieldId} is require
|
|
|
49
38
|
return error;
|
|
50
39
|
}
|
|
51
40
|
|
|
52
|
-
function createMembershipNotEligibleError(joinCheck) {
|
|
53
|
-
const error = new Error('membership_not_eligible');
|
|
54
|
-
error.code = 'membership_not_eligible';
|
|
55
|
-
error.status = 422;
|
|
56
|
-
error.responseBody = {
|
|
57
|
-
error: error.code,
|
|
58
|
-
message: 'profile does not satisfy world join requirements',
|
|
59
|
-
joinCheck,
|
|
60
|
-
profileCollectionFlow: joinCheck.profileCollectionFlow || null,
|
|
61
|
-
};
|
|
62
|
-
return error;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
function createJoinNotEligibleError(joinCheck) {
|
|
66
|
-
const error = new Error('world_join_not_eligible');
|
|
67
|
-
error.code = 'world_join_not_eligible';
|
|
68
|
-
error.status = 422;
|
|
69
|
-
error.responseBody = {
|
|
70
|
-
error: error.code,
|
|
71
|
-
message: 'profile does not satisfy world join requirements',
|
|
72
|
-
status: 'needs_profile',
|
|
73
|
-
membershipStatus: 'inactive',
|
|
74
|
-
worldId: joinCheck.worldId,
|
|
75
|
-
normalizedProfile: joinCheck.normalizedProfile,
|
|
76
|
-
missingFields: joinCheck.missingFields,
|
|
77
|
-
nextMissingField: joinCheck.nextMissingField,
|
|
78
|
-
missingFieldGuidance: joinCheck.missingFieldGuidance,
|
|
79
|
-
nextAction: 'retry_join_world_after_profile_update',
|
|
80
|
-
};
|
|
81
|
-
return error;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
41
|
function normalizeAgentId(agentId) {
|
|
85
42
|
const normalized = String(agentId || '').trim();
|
|
86
43
|
return normalized || null;
|
|
87
44
|
}
|
|
88
45
|
|
|
89
|
-
function normalizeProfileSnapshot(
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
46
|
+
function normalizeProfileSnapshot(profileSnapshot = null, participantContextText = null) {
|
|
47
|
+
const base = profileSnapshot && typeof profileSnapshot === 'object' && !Array.isArray(profileSnapshot)
|
|
48
|
+
? { ...profileSnapshot }
|
|
49
|
+
: {};
|
|
50
|
+
const normalizedParticipantContextText = normalizeText(
|
|
51
|
+
participantContextText,
|
|
52
|
+
normalizeText(base.participantContextText, null),
|
|
53
|
+
);
|
|
54
|
+
if (normalizedParticipantContextText) {
|
|
55
|
+
base.participantContextText = normalizedParticipantContextText;
|
|
94
56
|
}
|
|
95
|
-
return
|
|
57
|
+
return base;
|
|
96
58
|
}
|
|
97
59
|
|
|
98
|
-
function buildNextStageSummary(
|
|
99
|
-
const matchingSummary = world.matching.strategySummary
|
|
100
|
-
|| `The world uses ${world.matching.mode} matching to deliver candidate summaries before request_chat.`;
|
|
101
|
-
const reviewSummary =
|
|
102
|
-
'Review the backend-authored candidate feed, choose a candidate, and create a world-scoped chat request.';
|
|
103
|
-
const sessionSummary = `Matched agents then enter a ${world.sessionTemplate.mode} session with up to ${world.sessionTemplate.maxTurns} turns.`;
|
|
104
|
-
|
|
60
|
+
function buildNextStageSummary() {
|
|
105
61
|
return {
|
|
106
|
-
stage: '
|
|
107
|
-
summary:
|
|
108
|
-
|
|
109
|
-
matchingCadence: world.matching.cadence,
|
|
110
|
-
sessionMode: world.sessionTemplate.mode,
|
|
111
|
-
maxTurns: world.sessionTemplate.maxTurns,
|
|
112
|
-
turnTimeoutMs: world.sessionTemplate.turnTimeoutMs,
|
|
62
|
+
stage: 'candidate_review',
|
|
63
|
+
summary:
|
|
64
|
+
'Review the backend-authored candidate feed or use world search, then choose one target agent and create a world-scoped chat request.',
|
|
113
65
|
};
|
|
114
66
|
}
|
|
115
67
|
|
|
@@ -119,67 +71,86 @@ export function createMembershipService({ worldService, store = null } = {}) {
|
|
|
119
71
|
return store;
|
|
120
72
|
}
|
|
121
73
|
|
|
74
|
+
function resolveNormalizedParticipantContextText({ participantContextText = null, profileSnapshot = null, agent = null, world = null } = {}) {
|
|
75
|
+
return buildParticipantContextText({
|
|
76
|
+
world,
|
|
77
|
+
agent,
|
|
78
|
+
participantContextText,
|
|
79
|
+
profileSnapshot,
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
|
|
122
83
|
return {
|
|
123
|
-
evaluateJoin({ worldId,
|
|
84
|
+
evaluateJoin({ worldId, participantContextText = null } = {}) {
|
|
124
85
|
const world = worldService.requireWorld(worldId);
|
|
125
|
-
const
|
|
126
|
-
const
|
|
127
|
-
isEmptyValue(normalizedProfile[field.fieldId]),
|
|
128
|
-
);
|
|
129
|
-
const missingFields = orderedMissingFields.map((field) => projectMissingField(field));
|
|
130
|
-
const nextMissingField = missingFields[0] || null;
|
|
86
|
+
const normalizedParticipantContextText = normalizeText(participantContextText, null);
|
|
87
|
+
const accepted = Boolean(normalizedParticipantContextText);
|
|
131
88
|
|
|
132
|
-
|
|
89
|
+
return {
|
|
133
90
|
worldId: world.worldId,
|
|
134
|
-
accepted
|
|
135
|
-
status:
|
|
136
|
-
|
|
137
|
-
|
|
91
|
+
accepted,
|
|
92
|
+
status: accepted ? 'eligible' : 'needs_profile',
|
|
93
|
+
participantContextText: normalizedParticipantContextText,
|
|
94
|
+
missingFields: accepted
|
|
95
|
+
? []
|
|
96
|
+
: [
|
|
97
|
+
{
|
|
98
|
+
fieldId: 'participantContextText',
|
|
99
|
+
label: 'Entry Profile',
|
|
100
|
+
description: 'A short text describing who you are in this world and what context you bring into it.',
|
|
101
|
+
},
|
|
102
|
+
],
|
|
103
|
+
nextMissingField: accepted
|
|
104
|
+
? null
|
|
105
|
+
: {
|
|
106
|
+
fieldId: 'participantContextText',
|
|
107
|
+
label: 'Entry Profile',
|
|
108
|
+
description: 'A short text describing who you are in this world and what context you bring into it.',
|
|
109
|
+
},
|
|
138
110
|
missingFieldGuidance: {
|
|
139
|
-
mode:
|
|
140
|
-
orderedMissingFields:
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
111
|
+
mode: accepted ? 'complete' : 'single_text_field',
|
|
112
|
+
orderedMissingFields: accepted
|
|
113
|
+
? []
|
|
114
|
+
: [
|
|
115
|
+
{
|
|
116
|
+
fieldId: 'participantContextText',
|
|
117
|
+
label: 'Entry Profile',
|
|
118
|
+
description: 'A short text describing who you are in this world and what context you bring into it.',
|
|
119
|
+
},
|
|
120
|
+
],
|
|
121
|
+
orderedMissingFieldIds: accepted ? [] : ['participantContextText'],
|
|
122
|
+
nextMissingField: accepted
|
|
123
|
+
? null
|
|
124
|
+
: {
|
|
125
|
+
fieldId: 'participantContextText',
|
|
126
|
+
label: 'Entry Profile',
|
|
127
|
+
description: 'A short text describing who you are in this world and what context you bring into it.',
|
|
128
|
+
},
|
|
129
|
+
remainingRequiredFieldCount: accepted ? 0 : 1,
|
|
144
130
|
},
|
|
145
|
-
|
|
146
|
-
nextAction:
|
|
147
|
-
missingFields.length === 0 ? 'call_join_world' : 'retry_join_world_after_profile_update',
|
|
148
|
-
};
|
|
149
|
-
|
|
150
|
-
const worldDetail = worldService.describeWorldDetail(world.worldId);
|
|
151
|
-
return {
|
|
152
|
-
...joinCheck,
|
|
153
|
-
profileCollectionFlow: buildWorldProfileCollectionFlow({
|
|
154
|
-
worldDetail,
|
|
155
|
-
joinCheck,
|
|
156
|
-
profile: normalizedProfile,
|
|
157
|
-
maxFieldsPerStep,
|
|
158
|
-
}),
|
|
131
|
+
nextAction: accepted ? 'call_join_world' : 'retry_join_world_after_profile_update',
|
|
159
132
|
};
|
|
160
133
|
},
|
|
161
|
-
|
|
162
|
-
|
|
134
|
+
|
|
135
|
+
async createMembership({ worldId, agentId, participantContextText } = {}) {
|
|
136
|
+
const world = worldService.requireWorld(worldId);
|
|
163
137
|
const membershipStore = assertStore();
|
|
164
138
|
const normalizedAgentId = normalizeAgentId(agentId);
|
|
165
|
-
if (!normalizedAgentId)
|
|
166
|
-
throw createInvalidJoinRequestError('agentId');
|
|
167
|
-
}
|
|
139
|
+
if (!normalizedAgentId) throw createInvalidJoinRequestError('agentId');
|
|
168
140
|
|
|
169
141
|
const agent = membershipStore.getAgent(normalizedAgentId);
|
|
142
|
+
if (!agent) throw createAgentNotFoundError(normalizedAgentId);
|
|
170
143
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
const normalizedProfileSnapshot = normalizeProfileSnapshot(profileSnapshot, agent.profile);
|
|
176
|
-
const joinCheck = this.evaluateJoin({
|
|
177
|
-
worldId,
|
|
178
|
-
profile: normalizedProfileSnapshot,
|
|
144
|
+
const normalizedParticipantContextText = resolveNormalizedParticipantContextText({
|
|
145
|
+
world,
|
|
146
|
+
agent,
|
|
147
|
+
participantContextText,
|
|
179
148
|
});
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
149
|
+
if (!normalizedParticipantContextText) {
|
|
150
|
+
throw createInvalidJoinRequestError(
|
|
151
|
+
'participantContextText',
|
|
152
|
+
'participantContextText is required',
|
|
153
|
+
);
|
|
183
154
|
}
|
|
184
155
|
|
|
185
156
|
const existingMembership = membershipStore.listMemberships({
|
|
@@ -195,34 +166,37 @@ export function createMembershipService({ worldService, store = null } = {}) {
|
|
|
195
166
|
worldId,
|
|
196
167
|
agentId: normalizedAgentId,
|
|
197
168
|
status: 'joined',
|
|
198
|
-
profileSnapshot:
|
|
169
|
+
profileSnapshot: normalizeProfileSnapshot(null, normalizedParticipantContextText),
|
|
170
|
+
participantContextText: normalizedParticipantContextText,
|
|
199
171
|
});
|
|
200
172
|
|
|
201
173
|
return { membership, created: true };
|
|
202
174
|
},
|
|
203
|
-
|
|
175
|
+
|
|
176
|
+
async joinWorld({
|
|
177
|
+
worldId,
|
|
178
|
+
agentId,
|
|
179
|
+
participantContextText,
|
|
180
|
+
} = {}) {
|
|
204
181
|
const world = worldService.requireWorld(worldId);
|
|
205
182
|
const membershipStore = assertStore();
|
|
206
183
|
const normalizedAgentId = normalizeAgentId(agentId);
|
|
207
184
|
|
|
208
|
-
if (!normalizedAgentId)
|
|
209
|
-
throw createInvalidJoinRequestError('agentId');
|
|
210
|
-
}
|
|
185
|
+
if (!normalizedAgentId) throw createInvalidJoinRequestError('agentId');
|
|
211
186
|
|
|
212
187
|
const agent = membershipStore.getAgent(normalizedAgentId);
|
|
213
|
-
if (!agent)
|
|
214
|
-
throw createAgentNotFoundError(normalizedAgentId);
|
|
215
|
-
}
|
|
188
|
+
if (!agent) throw createAgentNotFoundError(normalizedAgentId);
|
|
216
189
|
|
|
217
|
-
const
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
maxFieldsPerStep,
|
|
190
|
+
const normalizedParticipantContextText = resolveNormalizedParticipantContextText({
|
|
191
|
+
world,
|
|
192
|
+
agent,
|
|
193
|
+
participantContextText,
|
|
222
194
|
});
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
195
|
+
if (!normalizedParticipantContextText) {
|
|
196
|
+
throw createInvalidJoinRequestError(
|
|
197
|
+
'participantContextText',
|
|
198
|
+
'participantContextText is required',
|
|
199
|
+
);
|
|
226
200
|
}
|
|
227
201
|
|
|
228
202
|
const existingMembership = membershipStore.listMemberships({
|
|
@@ -233,49 +207,53 @@ export function createMembershipService({ worldService, store = null } = {}) {
|
|
|
233
207
|
const membership = existingMembership
|
|
234
208
|
? await membershipStore.updateMembership(existingMembership.membershipId, {
|
|
235
209
|
status: 'active',
|
|
236
|
-
profileSnapshot:
|
|
210
|
+
profileSnapshot: normalizeProfileSnapshot(existingMembership.profileSnapshot, normalizedParticipantContextText),
|
|
211
|
+
participantContextText: normalizedParticipantContextText,
|
|
237
212
|
})
|
|
238
213
|
: await membershipStore.createMembership({
|
|
239
214
|
worldId: world.worldId,
|
|
240
215
|
agentId: normalizedAgentId,
|
|
241
216
|
status: 'active',
|
|
242
|
-
profileSnapshot:
|
|
217
|
+
profileSnapshot: normalizeProfileSnapshot(null, normalizedParticipantContextText),
|
|
218
|
+
participantContextText: normalizedParticipantContextText,
|
|
243
219
|
});
|
|
244
220
|
|
|
245
|
-
|
|
221
|
+
const joinResult = {
|
|
246
222
|
status: 'joined',
|
|
247
223
|
worldId: world.worldId,
|
|
248
224
|
membership,
|
|
249
225
|
membershipStatus: membership.status,
|
|
250
226
|
created: !existingMembership,
|
|
251
|
-
|
|
227
|
+
participantContextText: normalizedParticipantContextText,
|
|
252
228
|
nextAction: 'review_candidate_feed',
|
|
253
|
-
nextStageSummary: buildNextStageSummary(
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
},
|
|
263
|
-
}),
|
|
229
|
+
nextStageSummary: buildNextStageSummary(),
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
return {
|
|
233
|
+
...joinResult,
|
|
234
|
+
orchestration: buildResolvedWorldJoinOrchestration({
|
|
235
|
+
joinResult,
|
|
236
|
+
candidateDelivery: null,
|
|
237
|
+
}) || null,
|
|
264
238
|
};
|
|
265
239
|
},
|
|
240
|
+
|
|
266
241
|
getMembership({ worldId, agentId, includeDisabled = false } = {}) {
|
|
267
242
|
const normalizedAgentId = normalizeAgentId(agentId);
|
|
268
243
|
if (!normalizedAgentId) return null;
|
|
269
244
|
worldService.requireWorld(worldId, { includeDisabled });
|
|
270
245
|
return assertStore().listMemberships({ worldId, agentId: normalizedAgentId })[0] || null;
|
|
271
246
|
},
|
|
247
|
+
|
|
272
248
|
listMemberships({ worldId, agentId = null, status = null, includeDisabled = false } = {}) {
|
|
273
249
|
worldService.requireWorld(worldId, { includeDisabled });
|
|
274
250
|
return assertStore().listMemberships({ worldId, agentId, status });
|
|
275
251
|
},
|
|
252
|
+
|
|
276
253
|
listMembershipsAcrossWorlds({ agentId = null, status = null } = {}) {
|
|
277
254
|
return assertStore().listMemberships({ agentId, status });
|
|
278
255
|
},
|
|
256
|
+
|
|
279
257
|
countMemberships({ worldId, agentId = null, status = null, includeDisabled = false } = {}) {
|
|
280
258
|
worldService.requireWorld(worldId, { includeDisabled });
|
|
281
259
|
return assertStore().countMemberships({ worldId, agentId, status });
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
|
|
10
10
|
const DEFAULT_INSTALL_CHANNEL_ID = 'claworld';
|
|
11
11
|
const DEFAULT_INSTALL_ACCOUNT_ID = 'claworld';
|
|
12
|
-
const DEFAULT_INSTALL_LOCAL_AGENT_ID = '
|
|
12
|
+
const DEFAULT_INSTALL_LOCAL_AGENT_ID = 'main';
|
|
13
13
|
const DEFAULT_ACTIVATION_DISPLAY_NAME = 'Claworld Agent';
|
|
14
14
|
|
|
15
15
|
function normalizeText(value, fallback = null) {
|
|
@@ -174,7 +174,7 @@ export function createOnboardingService({ worldService, store = null } = {}) {
|
|
|
174
174
|
`run ${CLAWORLD_INSTALLER_COMMAND}`,
|
|
175
175
|
'installer validates OpenClaw availability and minimum host version',
|
|
176
176
|
'installer verifies or installs the claworld OpenClaw plugin package',
|
|
177
|
-
'installer writes or refreshes the managed claworld channel config',
|
|
177
|
+
'installer writes or refreshes the managed claworld channel config and binds it to the local main agent by default',
|
|
178
178
|
'installer calls POST /v1/onboarding/activate to obtain agentId and appToken when reuse is not possible',
|
|
179
179
|
'installer persists the returned appToken into the managed claworld account config',
|
|
180
180
|
'installer reloads or starts the runtime and verifies the relay binding',
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { createSystemMessageOrchestrator } from './world-conversation-text.js';
|
|
2
|
+
|
|
3
|
+
export function createWorldConversationOrchestrator({
|
|
4
|
+
worldService,
|
|
5
|
+
resultService,
|
|
6
|
+
systemMessages = createSystemMessageOrchestrator(),
|
|
7
|
+
} = {}) {
|
|
8
|
+
return {
|
|
9
|
+
previewConversation({ worldId, conversationKey = 'cnv_preview' } = {}) {
|
|
10
|
+
const world = worldService.requireWorld(worldId);
|
|
11
|
+
const openingPlan = systemMessages.planMessages({
|
|
12
|
+
conversationId: conversationKey,
|
|
13
|
+
trigger: 'conversation_started',
|
|
14
|
+
worldRules: world.conversationTemplate.worldRules,
|
|
15
|
+
});
|
|
16
|
+
const convergencePlan = [];
|
|
17
|
+
|
|
18
|
+
return {
|
|
19
|
+
worldId: world.worldId,
|
|
20
|
+
conversationTemplate: {
|
|
21
|
+
mode: world.conversationTemplate.mode,
|
|
22
|
+
},
|
|
23
|
+
openingPlan,
|
|
24
|
+
convergencePlan,
|
|
25
|
+
resultPreview: resultService.previewConversation({ world, conversationKey }),
|
|
26
|
+
status: 'preview_ready',
|
|
27
|
+
};
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
}
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
const DEFAULT_TEMPLATE_REFS = {
|
|
2
|
+
opening: 'world.conversation.opening',
|
|
3
|
+
convergence: 'world.conversation.convergence',
|
|
4
|
+
stateChanged: 'world.conversation.state_changed',
|
|
5
|
+
};
|
|
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 normalizeInteger(value, fallback = null) {
|
|
14
|
+
const normalized = Number(value);
|
|
15
|
+
if (!Number.isFinite(normalized)) return fallback;
|
|
16
|
+
return Math.trunc(normalized);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function normalizePositiveInteger(value, fallback = null) {
|
|
20
|
+
const normalized = normalizeInteger(value, fallback);
|
|
21
|
+
if (normalized == null || normalized <= 0) return fallback;
|
|
22
|
+
return normalized;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function normalizeTurnRule(rule = {}, index = 0) {
|
|
26
|
+
return {
|
|
27
|
+
id: rule.id || `turn_rule_${index + 1}`,
|
|
28
|
+
trigger: rule.trigger || 'turn_threshold',
|
|
29
|
+
atTurn: Number.isFinite(Number(rule.atTurn)) ? Math.max(0, Number(rule.atTurn)) : null,
|
|
30
|
+
visibility: rule.visibility || 'both',
|
|
31
|
+
role: rule.role || 'system',
|
|
32
|
+
templateRef: rule.templateRef || `world.turn.rule.${index + 1}`,
|
|
33
|
+
text: rule.text || null,
|
|
34
|
+
once: rule.once !== false,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function buildMessage({
|
|
39
|
+
conversationId = null,
|
|
40
|
+
trigger,
|
|
41
|
+
role = 'system',
|
|
42
|
+
visibility = 'both',
|
|
43
|
+
templateRef = null,
|
|
44
|
+
text = null,
|
|
45
|
+
metadata = {},
|
|
46
|
+
}) {
|
|
47
|
+
return {
|
|
48
|
+
conversationId: normalizeText(conversationId, null),
|
|
49
|
+
trigger,
|
|
50
|
+
role,
|
|
51
|
+
visibility,
|
|
52
|
+
templateRef,
|
|
53
|
+
text,
|
|
54
|
+
metadata,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function formatConversationOverview(detail = {}) {
|
|
59
|
+
const conversationOverview = detail.conversationOverview && typeof detail.conversationOverview === 'object'
|
|
60
|
+
? detail.conversationOverview
|
|
61
|
+
: {};
|
|
62
|
+
const mode = normalizeText(detail.conversationMode || conversationOverview.mode, null);
|
|
63
|
+
const parts = [];
|
|
64
|
+
|
|
65
|
+
if (mode) parts.push(`${mode} mode`);
|
|
66
|
+
|
|
67
|
+
return parts.length > 0 ? parts.join(', ') : null;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function buildWorldConversationContextEvent(detail = {}) {
|
|
71
|
+
const worldId = normalizeText(detail.worldId || detail.world?.worldId, null);
|
|
72
|
+
if (!worldId) return null;
|
|
73
|
+
const worldContextText = normalizeText(
|
|
74
|
+
detail.world?.worldContextText,
|
|
75
|
+
normalizeText(detail.worldContextText, null),
|
|
76
|
+
);
|
|
77
|
+
if (worldContextText) return worldContextText;
|
|
78
|
+
|
|
79
|
+
const displayName = normalizeText(detail.displayName || detail.world?.displayName || detail.worldDisplayName, worldId);
|
|
80
|
+
const summary = normalizeText(detail.summary || detail.world?.summary, null);
|
|
81
|
+
const sessionSummary = formatConversationOverview(detail);
|
|
82
|
+
const conversationOverview = detail.conversationOverview && typeof detail.conversationOverview === 'object'
|
|
83
|
+
? detail.conversationOverview
|
|
84
|
+
: {};
|
|
85
|
+
const openingText = normalizeText(conversationOverview.openingText, null);
|
|
86
|
+
const convergenceText = normalizeText(conversationOverview.convergence?.text, null);
|
|
87
|
+
const interactionRules = normalizeText(detail.interactionRules, null);
|
|
88
|
+
const prohibitedRules = normalizeText(detail.prohibitedRules, null);
|
|
89
|
+
const ratingRules = normalizeText(detail.ratingRules, null);
|
|
90
|
+
|
|
91
|
+
const lines = [
|
|
92
|
+
'Internal Claworld world context for this conversation.',
|
|
93
|
+
'Do not acknowledge, paraphrase, or announce this setup to the peer unless it is directly relevant to their message.',
|
|
94
|
+
`World: ${displayName} [${worldId}]`,
|
|
95
|
+
summary ? `Summary: ${summary}` : null,
|
|
96
|
+
sessionSummary ? `Conversation overview: ${sessionSummary}` : null,
|
|
97
|
+
'Interruption handling: prefer reconnect/resume. Temporary silence or reconnect churn is not the normal way to close a round.',
|
|
98
|
+
openingText ? `Opening focus: ${openingText}` : null,
|
|
99
|
+
interactionRules ? `Interaction rules: ${interactionRules}` : null,
|
|
100
|
+
prohibitedRules ? `Prohibited rules: ${prohibitedRules}` : null,
|
|
101
|
+
ratingRules ? `Rating rules: ${ratingRules}` : null,
|
|
102
|
+
convergenceText ? `Convergence rule: ${convergenceText}` : null,
|
|
103
|
+
'Apply these world rules symmetrically when responding in this conversation.',
|
|
104
|
+
].filter(Boolean);
|
|
105
|
+
|
|
106
|
+
return lines.join('\n');
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export function createSystemMessageOrchestrator({ templateRefs = DEFAULT_TEMPLATE_REFS } = {}) {
|
|
110
|
+
return {
|
|
111
|
+
supportedTriggers: ['conversation_started', 'turn_threshold', 'convergence', 'state_changed'],
|
|
112
|
+
describeRuleShape() {
|
|
113
|
+
return {
|
|
114
|
+
opening_system_message: 'optional text/template ref',
|
|
115
|
+
turn_message_rules: [
|
|
116
|
+
{
|
|
117
|
+
id: 'turn_nudge_2',
|
|
118
|
+
trigger: 'turn_threshold',
|
|
119
|
+
atTurn: 2,
|
|
120
|
+
visibility: 'both',
|
|
121
|
+
role: 'system',
|
|
122
|
+
templateRef: 'world.turn.nudge',
|
|
123
|
+
once: true,
|
|
124
|
+
},
|
|
125
|
+
],
|
|
126
|
+
convergence_message: {
|
|
127
|
+
whenRemainingTurnsLTE: 1,
|
|
128
|
+
templateRef: templateRefs.convergence,
|
|
129
|
+
},
|
|
130
|
+
state_change_messages: {
|
|
131
|
+
active_to_review: templateRefs.stateChanged,
|
|
132
|
+
},
|
|
133
|
+
};
|
|
134
|
+
},
|
|
135
|
+
planMessages({
|
|
136
|
+
conversationId = null,
|
|
137
|
+
trigger = 'conversation_started',
|
|
138
|
+
turnIndex = 0,
|
|
139
|
+
remainingTurns = null,
|
|
140
|
+
worldRules = {},
|
|
141
|
+
previousState = null,
|
|
142
|
+
nextState = null,
|
|
143
|
+
emittedRuleIds = [],
|
|
144
|
+
} = {}) {
|
|
145
|
+
const resolvedConversationId = normalizeText(conversationId, null);
|
|
146
|
+
const messages = [];
|
|
147
|
+
const emitted = new Set(emittedRuleIds);
|
|
148
|
+
|
|
149
|
+
if (trigger === 'conversation_started' && worldRules.openingSystemMessage !== false) {
|
|
150
|
+
messages.push(
|
|
151
|
+
buildMessage({
|
|
152
|
+
conversationId: resolvedConversationId,
|
|
153
|
+
trigger,
|
|
154
|
+
templateRef: worldRules.openingTemplateRef || templateRefs.opening,
|
|
155
|
+
text: worldRules.openingText || null,
|
|
156
|
+
metadata: { phase: 'opening' },
|
|
157
|
+
}),
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const rules = Array.isArray(worldRules.turnMessageRules)
|
|
162
|
+
? worldRules.turnMessageRules.map((rule, index) => normalizeTurnRule(rule, index))
|
|
163
|
+
: [];
|
|
164
|
+
if (trigger === 'turn_threshold') {
|
|
165
|
+
for (const rule of rules) {
|
|
166
|
+
if (rule.trigger !== 'turn_threshold') continue;
|
|
167
|
+
if (rule.atTurn == null || turnIndex < rule.atTurn) continue;
|
|
168
|
+
if (rule.once && emitted.has(rule.id)) continue;
|
|
169
|
+
messages.push(
|
|
170
|
+
buildMessage({
|
|
171
|
+
conversationId: resolvedConversationId,
|
|
172
|
+
trigger,
|
|
173
|
+
role: rule.role,
|
|
174
|
+
visibility: rule.visibility,
|
|
175
|
+
templateRef: rule.templateRef,
|
|
176
|
+
text: rule.text,
|
|
177
|
+
metadata: { ruleId: rule.id, atTurn: rule.atTurn },
|
|
178
|
+
}),
|
|
179
|
+
);
|
|
180
|
+
emitted.add(rule.id);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const convergenceThreshold = Number.isFinite(Number(worldRules.convergence?.whenRemainingTurnsLTE))
|
|
185
|
+
? Number(worldRules.convergence.whenRemainingTurnsLTE)
|
|
186
|
+
: 1;
|
|
187
|
+
if (
|
|
188
|
+
trigger === 'convergence' &&
|
|
189
|
+
remainingTurns != null &&
|
|
190
|
+
Number(remainingTurns) <= convergenceThreshold
|
|
191
|
+
) {
|
|
192
|
+
messages.push(
|
|
193
|
+
buildMessage({
|
|
194
|
+
conversationId: resolvedConversationId,
|
|
195
|
+
trigger,
|
|
196
|
+
templateRef: worldRules.convergence?.templateRef || templateRefs.convergence,
|
|
197
|
+
text: worldRules.convergence?.text || null,
|
|
198
|
+
metadata: { remainingTurns: Number(remainingTurns) },
|
|
199
|
+
}),
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if (trigger === 'state_changed' && previousState !== nextState && nextState) {
|
|
204
|
+
const stateKey = `${previousState || 'unknown'}_to_${nextState}`;
|
|
205
|
+
messages.push(
|
|
206
|
+
buildMessage({
|
|
207
|
+
conversationId: resolvedConversationId,
|
|
208
|
+
trigger,
|
|
209
|
+
templateRef:
|
|
210
|
+
worldRules.stateChangeMessages?.[stateKey]?.templateRef || templateRefs.stateChanged,
|
|
211
|
+
text: worldRules.stateChangeMessages?.[stateKey]?.text || null,
|
|
212
|
+
metadata: {
|
|
213
|
+
previousState,
|
|
214
|
+
nextState,
|
|
215
|
+
stateKey,
|
|
216
|
+
},
|
|
217
|
+
}),
|
|
218
|
+
);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
return {
|
|
222
|
+
conversationId: resolvedConversationId,
|
|
223
|
+
turnIndex,
|
|
224
|
+
trigger,
|
|
225
|
+
emittedRuleIds: [...emitted],
|
|
226
|
+
messages,
|
|
227
|
+
status: messages.length > 0 ? 'planned' : 'noop',
|
|
228
|
+
};
|
|
229
|
+
},
|
|
230
|
+
};
|
|
231
|
+
}
|
|
@@ -3,13 +3,19 @@ import { createCanonicalResultBuilder } from '../../openclaw/runtime/canonical-r
|
|
|
3
3
|
export function createResultService({ builder = createCanonicalResultBuilder() } = {}) {
|
|
4
4
|
return {
|
|
5
5
|
schema: builder.schema,
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
previewConversation({ world, conversationKey = 'cnv_preview' } = {}) {
|
|
7
|
+
const preview = builder.build({
|
|
8
|
+
conversationId: conversationKey,
|
|
9
9
|
intentSignals: world.resultContract.exampleSignals.intentSignals,
|
|
10
10
|
conversationSignals: world.resultContract.exampleSignals.conversationSignals,
|
|
11
11
|
agentSignals: world.resultContract.exampleSignals.agentSignals,
|
|
12
12
|
});
|
|
13
|
+
const rest = { ...preview };
|
|
14
|
+
delete rest.conversationId;
|
|
15
|
+
return {
|
|
16
|
+
...rest,
|
|
17
|
+
conversationKey,
|
|
18
|
+
};
|
|
13
19
|
},
|
|
14
20
|
};
|
|
15
21
|
}
|