@xfxstudio/claworld 0.2.5 → 0.2.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (29) hide show
  1. package/openclaw.plugin.json +1 -1
  2. package/package.json +1 -1
  3. package/skills/claworld-help/SKILL.md +2 -2
  4. package/skills/claworld-join-and-chat/SKILL.md +18 -9
  5. package/src/lib/chat-request.js +19 -0
  6. package/src/lib/relay/kickoff-text.js +6 -1
  7. package/src/openclaw/installer/core.js +16 -2
  8. package/src/openclaw/plugin/claworld-channel-plugin.js +164 -12
  9. package/src/openclaw/plugin/config-schema.js +9 -1
  10. package/src/openclaw/plugin/register.js +151 -15
  11. package/src/openclaw/plugin/relay-client.js +502 -1
  12. package/src/openclaw/runtime/demo-session-bootstrap.js +1 -2
  13. package/src/openclaw/runtime/tool-contracts.js +40 -1
  14. package/src/openclaw/runtime/tool-inventory.js +3 -3
  15. package/src/openclaw/runtime/world-moderation-helper.js +9 -13
  16. package/src/product-shell/catalog/default-world-catalog.js +12 -258
  17. package/src/product-shell/contracts/world-manifest.js +0 -38
  18. package/src/product-shell/contracts/world-orchestration.js +0 -6
  19. package/src/product-shell/index.js +1 -5
  20. package/src/product-shell/membership/membership-service.js +24 -6
  21. package/src/product-shell/orchestration/world-conversation-orchestrator.js +0 -2
  22. package/src/product-shell/orchestration/world-conversation-text.js +0 -2
  23. package/src/product-shell/social/chat-request-routes.js +24 -1
  24. package/src/product-shell/social/chat-request-service.js +185 -15
  25. package/src/product-shell/worlds/world-admin-service.js +28 -120
  26. package/src/product-shell/worlds/world-authorization.js +20 -17
  27. package/src/product-shell/worlds/world-broadcast-service.js +2 -5
  28. package/src/product-shell/worlds/world-text.js +0 -2
  29. package/src/product-shell/results/result-service.js +0 -21
@@ -2,283 +2,37 @@ export const DEFAULT_WORLD_MANIFESTS = Object.freeze([
2
2
  {
3
3
  worldId: 'dating-demo-world',
4
4
  displayName: 'Dating Demo World',
5
- summary: 'Mutual-interest matching world for proving the A2A conversation loop before human handoff.',
5
+ summary: 'A lightweight social world for proving world-scoped join, review, and chat request flow.',
6
6
  description:
7
- 'A lightweight social world for people who want their agents to screen for mutual fit before deciding whether a human handoff is worthwhile.',
7
+ 'A lightweight social world where agents compare fit briefly before deciding whether to continue beyond agent chat.',
8
8
  category: 'social',
9
9
  lifecycle: 'prototype',
10
10
  tags: ['dating', 'matching', 'a2a'],
11
- interactionRules:
12
- 'Both agents should clarify fit quickly, stay respectful, avoid over-sharing, and stop once both sides can decide whether to continue beyond agent chat.',
13
- prohibitedRules:
14
- 'Do not pressure, harass, manipulate, or ask for unsafe personal details. Do not continue pushing once the other side clearly signals discomfort or disinterest.',
15
- ratingRules:
16
- 'When the interaction ends, each agent should rate the other side from 1 to 10 based on mutual fit, conversational quality, and respect for the world rules.',
17
- roles: [
18
- {
19
- roleId: 'seeker',
20
- label: 'Seeker',
21
- objective: 'Find a compatible person and decide whether to raise hand.',
22
- promptSummary: 'Represent your human owner faithfully and optimize for respectful fit.',
23
- },
24
- {
25
- roleId: 'candidate',
26
- label: 'Candidate',
27
- objective: 'Assess alignment and decide whether to continue beyond agent-only chat.',
28
- promptSummary: 'Share enough context to evaluate fit while staying concise and safe.',
29
- },
30
- ],
31
- joinSchema: {
32
- requiredFields: [
33
- {
34
- fieldId: 'headline',
35
- label: 'Headline',
36
- description: 'One-line self introduction shown during initial discovery.',
37
- examples: ['Shanghai-based product lead who likes trail running'],
38
- },
39
- {
40
- fieldId: 'intent',
41
- label: 'Intent',
42
- description: 'What kind of connection the user is open to.',
43
- examples: ['serious relationship', 'new friends first'],
44
- },
45
- {
46
- fieldId: 'location',
47
- label: 'Location',
48
- description: 'Current city or region used for basic filtering.',
49
- examples: ['Shanghai'],
50
- },
51
- ],
52
- optionalFields: [
53
- {
54
- fieldId: 'interests',
55
- label: 'Interests',
56
- type: 'string[]',
57
- description: 'Interests or hobbies used for match prompts.',
58
- examples: ['running', 'indie films', 'cats'],
59
- },
60
- {
61
- fieldId: 'conversationStyle',
62
- label: 'Conversation Style',
63
- description: 'Tone preference for the agent during A2A chat.',
64
- examples: ['playful but direct'],
65
- },
66
- ],
67
- hints: [
68
- 'Keep profile fields concrete so both matching and prompt injection stay stable.',
69
- 'World-level rules should decide when two agents have effectively reached a human handoff threshold.',
70
- ],
71
- },
72
- searchSchema: {
73
- mode: 'profile_overlap_search',
74
- inputFieldIds: ['intent', 'location', 'interests'],
75
- summary:
76
- 'Compatibility-only manual search over active online members by intent, location, and shared interests. Candidate feed review is the canonical path before request_chat.',
77
- hints: [
78
- 'Search defaults to the viewer membership profile when no explicit query is provided.',
79
- 'Only online members with an active world membership are returned.',
80
- ],
81
- },
82
- matching: {
83
- mode: 'scored_push',
84
- cadence: 'periodic',
85
- strategySummary:
86
- 'Score active online memberships by intent, location, and interests, deliver candidate summaries first, and route live contact through request_chat after review.',
87
- candidateSources: ['active_memberships_online'],
88
- },
89
- conversationTemplate: {
90
- mode: 'a2a',
91
- worldRules: {
92
- openingText: 'You are in the dating demo world. Clarify fit quickly and stop once both sides can decide whether to continue.',
93
- turnMessageRules: [{ id: 'nudge-2', atTurn: 2, templateRef: 'world.turn.nudge' }],
94
- convergence: {
95
- whenRemainingTurnsLTE: 1,
96
- text: 'Focus on unresolved blockers and whether both sides have enough information to decide next steps.',
97
- },
98
- },
99
- },
100
- resultContract: {
101
- schemaId: 'dating-demo-world.result.v1',
102
- outputs: ['match_score', 'recommendation', 'risks', 'evidence'],
103
- successCriteria: ['both agents can summarize fit', 'next step is explicit'],
104
- exampleSignals: {
105
- intentSignals: [{ id: 'intent-1', type: 'intent_match', score: 0.8, summary: 'Stated intent aligns.' }],
106
- conversationSignals: [
107
- { id: 'conv-1', type: 'next_step_ready', score: 0.7, summary: 'One side is ready to stop and move forward.' },
108
- { id: 'conv-2', type: 'next_step_ready', score: 0.7, summary: 'The other side is also ready to stop.' },
109
- ],
110
- agentSignals: [{ id: 'agent-1', type: 'safety', risk: 0.1, summary: 'No major risk surfaced.' }],
111
- },
112
- },
11
+ worldContextText:
12
+ '世界:Dating Demo World [dating-demo-world]\n简介:A lightweight social world for proving world-scoped join, review, and chat request flow.\n互动规则:Clarify fit quickly, stay respectful, and stop once next steps are clear.\n禁止事项:Do not pressure, harass, manipulate, or ask for unsafe personal details.',
113
13
  },
114
14
  {
115
15
  worldId: 'skill-handoff-world',
116
16
  displayName: 'Skill Handoff World',
117
- summary: 'Supply-demand world for small scoped skill requests before full project delivery and escrow are introduced.',
17
+ summary: 'A small scoped skill-handoff world for proving service-fit review before direct contact.',
118
18
  description:
119
- 'A service marketplace world where agents help their owners quickly screen for delivery fit before moving to a direct human conversation.',
19
+ 'A service marketplace world where agents quickly screen delivery fit before moving to direct human contact.',
120
20
  category: 'marketplace',
121
21
  lifecycle: 'prototype',
122
22
  tags: ['skills', 'services', 'matching'],
123
- interactionRules:
124
- 'Agents should clarify scope, constraints, expected deliverables, and whether a direct handoff is justified. Keep the exchange concrete and decision-oriented.',
125
- prohibitedRules:
126
- 'Do not misrepresent capabilities, hide obvious delivery blockers, or pressure the other side into a commitment without clear scope alignment.',
127
- ratingRules:
128
- 'At the end of the exchange, each agent should rate the other side from 1 to 10 based on scope clarity, responsiveness, and confidence in a productive handoff.',
129
- roles: [
130
- {
131
- roleId: 'buyer',
132
- label: 'Buyer',
133
- objective: 'Describe a task crisply enough for rapid screening.',
134
- },
135
- {
136
- roleId: 'seller',
137
- label: 'Seller',
138
- objective: 'Assess fit, delivery scope, and whether to continue to a human handoff.',
139
- },
140
- ],
141
- joinSchema: {
142
- requiredFields: [
143
- {
144
- fieldId: 'headline',
145
- label: 'Headline',
146
- description: 'What the user can buy or sell in one line.',
147
- },
148
- {
149
- fieldId: 'capabilities',
150
- label: 'Capabilities',
151
- type: 'string[]',
152
- description: 'Skills or request categories used during matching.',
153
- examples: ['typescript', 'prompt design', 'landing page'],
154
- },
155
- ],
156
- optionalFields: [
157
- {
158
- fieldId: 'budgetBand',
159
- label: 'Budget Band',
160
- description: 'Optional budget or rate band for screening.',
161
- examples: ['200-500 USD', '50 USD/hour'],
162
- },
163
- ],
164
- hints: ['Use this world to prove supply-demand matching before implementing transaction settlement.'],
165
- },
166
- searchSchema: {
167
- mode: 'capability_overlap_search',
168
- inputFieldIds: ['capabilities', 'budgetBand'],
169
- summary:
170
- 'Compatibility-only manual search over active online members by capability overlap and optional budget fit. Candidate feed review is the canonical path before request_chat.',
171
- },
172
- matching: {
173
- mode: 'intent_filter',
174
- cadence: 'on_demand',
175
- strategySummary:
176
- 'Filter active online world members by capability overlap, deliver candidate summaries first, and let members request_chat before negotiating fit in a short session.',
177
- candidateSources: ['active_memberships_online'],
178
- },
179
- conversationTemplate: {
180
- mode: 'a2a',
181
- worldRules: {
182
- openingText: 'Clarify scope, constraints, and whether a human handoff is justified.',
183
- },
184
- },
185
- resultContract: {
186
- schemaId: 'skill-handoff-world.result.v1',
187
- outputs: ['recommendation', 'risks', 'evidence'],
188
- successCriteria: ['scope is summarized', 'handoff recommendation is explicit'],
189
- exampleSignals: {
190
- intentSignals: [{ id: 'intent-1', type: 'intent_match', score: 0.65, summary: 'Capabilities appear relevant.' }],
191
- conversationSignals: [{ id: 'conv-1', type: 'human_handoff_ready', score: 0.6, summary: 'Seller is ready for human handoff.' }],
192
- agentSignals: [{ id: 'agent-1', type: 'scope_risk', risk: 0.2, summary: 'A few requirements remain vague.' }],
193
- },
194
- },
23
+ worldContextText:
24
+ '世界:Skill Handoff World [skill-handoff-world]\n简介:A service marketplace world where agents screen delivery fit before moving to direct human contact.\n互动规则:Clarify scope, constraints, and whether a handoff is justified.\n禁止事项:Do not misrepresent capabilities or hide delivery blockers.',
195
25
  },
196
26
  {
197
27
  worldId: 'job-match-world',
198
28
  displayName: 'Job Match World',
199
- summary: 'Recruiter-candidate world optimized for profile completeness, matching, and concise A2A screening.',
29
+ summary: 'A hiring-fit world optimized for profile completeness, matching, and concise A2A screening.',
200
30
  description:
201
- 'A recruiting world for agent-assisted screening where candidates and recruiters validate fit before escalating to direct human contact.',
31
+ 'A recruiting world for agent-assisted screening where both sides validate fit before escalating to direct human contact.',
202
32
  category: 'recruiting',
203
33
  lifecycle: 'prototype',
204
34
  tags: ['jobs', 'recruiting', 'screening'],
205
- interactionRules:
206
- 'Agents should focus on role fit, experience relevance, hiring constraints, and whether a direct next step is justified. Keep the conversation concise and factual.',
207
- prohibitedRules:
208
- 'Do not fabricate experience, compensation expectations, or hiring authority. Do not request sensitive personal data unrelated to the role fit conversation.',
209
- ratingRules:
210
- 'When the interaction ends, each agent should rate the other side from 1 to 10 based on role fit, signal quality, and likelihood that a human follow-up is worthwhile.',
211
- roles: [
212
- {
213
- roleId: 'candidate',
214
- label: 'Candidate',
215
- objective: 'Surface fit and blockers quickly.',
216
- },
217
- {
218
- roleId: 'recruiter',
219
- label: 'Recruiter',
220
- objective: 'Determine whether to escalate to human contact.',
221
- },
222
- ],
223
- joinSchema: {
224
- requiredFields: [
225
- {
226
- fieldId: 'headline',
227
- label: 'Headline',
228
- description: 'Current role or target role.',
229
- },
230
- {
231
- fieldId: 'experienceSummary',
232
- label: 'Experience Summary',
233
- description: 'Condensed summary of experience relevant to matching.',
234
- },
235
- {
236
- fieldId: 'targetRole',
237
- label: 'Target Role',
238
- description: 'Role or hiring need that anchors matching.',
239
- },
240
- ],
241
- optionalFields: [
242
- {
243
- fieldId: 'location',
244
- label: 'Location',
245
- },
246
- {
247
- fieldId: 'workMode',
248
- label: 'Work Mode',
249
- description: 'Onsite, hybrid, or remote.',
250
- },
251
- ],
252
- hints: ['This world should eventually integrate search/browse, but the current shell only defines the contract.'],
253
- },
254
- searchSchema: {
255
- mode: 'profile_overlap_search',
256
- inputFieldIds: ['targetRole', 'location', 'workMode'],
257
- summary:
258
- 'Compatibility-only manual search over active online members by role fit, location, and work mode. Candidate feed review is the canonical path before request_chat.',
259
- },
260
- matching: {
261
- mode: 'profile_overlap',
262
- cadence: 'periodic',
263
- strategySummary:
264
- 'Use active online memberships plus target role, experience summary, and location/work mode as the first-pass scoring surface, deliver candidate summaries, and route contact through request_chat after review.',
265
- candidateSources: ['active_memberships_online'],
266
- },
267
- conversationTemplate: {
268
- mode: 'a2a',
269
- worldRules: {
270
- openingText: 'Focus on role fit, constraints, and whether both sides should move to a human conversation.',
271
- },
272
- },
273
- resultContract: {
274
- schemaId: 'job-match-world.result.v1',
275
- outputs: ['match_score', 'recommendation', 'risks', 'evidence'],
276
- successCriteria: ['role fit is explicit', 'human next step is explicit'],
277
- exampleSignals: {
278
- intentSignals: [{ id: 'intent-1', type: 'role_fit', score: 0.7, summary: 'Role expectations line up.' }],
279
- conversationSignals: [{ id: 'conv-1', type: 'next_step_ready', score: 0.55, summary: 'Both sides are ready to stop and proceed.' }],
280
- agentSignals: [{ id: 'agent-1', type: 'timeline_risk', risk: 0.15, summary: 'Availability still needs confirmation.' }],
281
- },
282
- },
35
+ worldContextText:
36
+ '世界:Job Match World [job-match-world]\n简介:A recruiting world for agent-assisted screening before direct human contact.\n互动规则:Focus on fit, constraints, and whether a next step is justified.\n禁止事项:Do not fabricate experience, compensation, or hiring authority.',
283
37
  },
284
38
  ]);
@@ -77,25 +77,6 @@ function normalizeField(field = {}, index = 0, { required = false } = {}) {
77
77
  };
78
78
  }
79
79
 
80
- function normalizeRole(role = {}, index = 0) {
81
- const roleId = normalizeText(role.roleId || role.id, `role_${index + 1}`);
82
- return {
83
- roleId,
84
- label: normalizeText(role.label, roleId),
85
- objective: normalizeText(role.objective, null),
86
- promptSummary: normalizeText(role.promptSummary, null),
87
- };
88
- }
89
-
90
- function normalizeExampleSignals(exampleSignals = {}) {
91
- const normalizeGroup = (key) => (Array.isArray(exampleSignals[key]) ? exampleSignals[key] : []);
92
- return {
93
- intentSignals: normalizeGroup('intentSignals'),
94
- conversationSignals: normalizeGroup('conversationSignals'),
95
- agentSignals: normalizeGroup('agentSignals'),
96
- };
97
- }
98
-
99
80
  function normalizeJoinSchema(joinSchema = {}) {
100
81
  return {
101
82
  requiredFields: [
@@ -177,10 +158,6 @@ export function normalizeWorldManifest(manifest = {}, index = 0) {
177
158
  const defaultInteractionRules = normalizeText(conversationTemplate?.worldRules?.openingText, null);
178
159
  const normalizedInteractionRules = normalizeText(manifest.interactionRules, defaultInteractionRules);
179
160
  const normalizedProhibitedRules = normalizeText(manifest.prohibitedRules, null);
180
- const normalizedRatingRules = normalizeText(
181
- manifest.ratingRules,
182
- 'After the interaction ends, each agent should rate the other participant from 1 to 10 based on fit, clarity, and rule compliance.',
183
- );
184
161
 
185
162
  return {
186
163
  worldId,
@@ -193,7 +170,6 @@ export function normalizeWorldManifest(manifest = {}, index = 0) {
193
170
  tags: normalizeStringList(manifest.tags),
194
171
  interactionRules: normalizedInteractionRules,
195
172
  prohibitedRules: normalizedProhibitedRules,
196
- ratingRules: normalizedRatingRules,
197
173
  worldContextText: buildWorldContextText({
198
174
  worldId,
199
175
  displayName: normalizeText(manifest.displayName, worldId),
@@ -204,10 +180,8 @@ export function normalizeWorldManifest(manifest = {}, index = 0) {
204
180
  ),
205
181
  interactionRules: normalizedInteractionRules,
206
182
  prohibitedRules: normalizedProhibitedRules,
207
- ratingRules: normalizedRatingRules,
208
183
  }),
209
184
  creatorAgentId: normalizeText(manifest.creatorAgentId, null),
210
- adminAgentIds: normalizeStringList(manifest.adminAgentIds),
211
185
  eligibility: normalizeWorldEligibility(manifest.eligibility, 'active'),
212
186
  broadcast: normalizeBroadcastConfig(manifest.broadcast, {
213
187
  enabled: manifest.broadcast?.enabled === true,
@@ -224,9 +198,6 @@ export function normalizeWorldManifest(manifest = {}, index = 0) {
224
198
  : 0,
225
199
  }
226
200
  : { totalConversationCount: 0 },
227
- roles: Array.isArray(manifest.roles)
228
- ? manifest.roles.map((role, roleIndex) => normalizeRole(role, roleIndex))
229
- : [],
230
201
  joinSchema,
231
202
  searchSchema,
232
203
  matching: {
@@ -246,12 +217,6 @@ export function normalizeWorldManifest(manifest = {}, index = 0) {
246
217
  stateChangeMessages: conversationTemplate?.worldRules?.stateChangeMessages || {},
247
218
  },
248
219
  },
249
- resultContract: {
250
- schemaId: normalizeText(manifest.resultContract?.schemaId, `${worldId}.result.v1`),
251
- outputs: normalizeStringList(manifest.resultContract?.outputs),
252
- successCriteria: normalizeStringList(manifest.resultContract?.successCriteria),
253
- exampleSignals: normalizeExampleSignals(manifest.resultContract?.exampleSignals),
254
- },
255
220
  meta: {
256
221
  status: normalizeText(manifest.meta?.status, 'scaffold_ready'),
257
222
  persistence: normalizeText(manifest.meta?.persistence, 'in_memory_catalog'),
@@ -325,7 +290,6 @@ export function projectWorldDetail(world) {
325
290
  summary: world.summary,
326
291
  interactionRules: world.interactionRules,
327
292
  prohibitedRules: world.prohibitedRules,
328
- ratingRules: world.ratingRules,
329
293
  conversationMode: world.conversationTemplate.mode,
330
294
  conversationOverview: {
331
295
  worldId: world.worldId,
@@ -373,7 +337,6 @@ export function projectConversationWorldContext(world) {
373
337
  summary: world.summary,
374
338
  interactionRules: world.interactionRules,
375
339
  prohibitedRules: world.prohibitedRules,
376
- ratingRules: world.ratingRules,
377
340
  conversationMode: world.conversationTemplate.mode,
378
341
  conversationOverview: {
379
342
  worldId: world.worldId,
@@ -392,7 +355,6 @@ export function projectConversationWorldContext(world) {
392
355
  worldContextText: runtimeWorldContext.text,
393
356
  interactionRules: world.interactionRules,
394
357
  prohibitedRules: world.prohibitedRules,
395
- ratingRules: world.ratingRules,
396
358
  conversationMode: world.conversationTemplate.mode,
397
359
  conversationOverview: {
398
360
  worldId: world.worldId,
@@ -173,8 +173,6 @@ function normalizeWorldDetail(payload = {}) {
173
173
  conversationMode: normalizeText(payload.conversationMode, 'a2a'),
174
174
  interactionRules: normalizeText(payload.interactionRules, null),
175
175
  prohibitedRules: normalizeText(payload.prohibitedRules, null),
176
- ratingRules: normalizeText(payload.ratingRules, null),
177
- adminAgentIds: normalizeStringList(payload.adminAgentIds),
178
176
  eligibility: normalizeText(payload.eligibility, 'active'),
179
177
  broadcast: normalizeBroadcastConfig(payload.broadcast),
180
178
  requiredFields,
@@ -236,8 +234,6 @@ function normalizeWorldDetail(payload = {}) {
236
234
  ),
237
235
  interactionRules: normalizeText(payload.interactionRules || world.interactionRules, null),
238
236
  prohibitedRules: normalizeText(payload.prohibitedRules || world.prohibitedRules, null),
239
- ratingRules: normalizeText(payload.ratingRules || world.ratingRules, null),
240
- adminAgentIds: normalizeStringList(payload.adminAgentIds || world.adminAgentIds),
241
237
  eligibility: normalizeText(payload.eligibility || world.eligibility, 'active'),
242
238
  broadcast: normalizeBroadcastConfig(payload.broadcast || world.broadcast),
243
239
  requiredFields,
@@ -424,7 +420,6 @@ export function buildWorldSessionStartupText(detail = {}) {
424
420
  const convergenceText = normalizeText(conversationOverview.convergence?.text, null);
425
421
  const interactionRules = normalizeText(normalizedDetail.interactionRules, null);
426
422
  const prohibitedRules = normalizeText(normalizedDetail.prohibitedRules, null);
427
- const ratingRules = normalizeText(normalizedDetail.ratingRules, null);
428
423
 
429
424
  const lines = [
430
425
  'Internal Claworld world context for this conversation.',
@@ -436,7 +431,6 @@ export function buildWorldSessionStartupText(detail = {}) {
436
431
  openingText ? `Opening focus: ${openingText}` : null,
437
432
  interactionRules ? `Interaction rules: ${interactionRules}` : null,
438
433
  prohibitedRules ? `Prohibited rules: ${prohibitedRules}` : null,
439
- ratingRules ? `Rating rules: ${ratingRules}` : null,
440
434
  convergenceText ? `Convergence rule: ${convergenceText}` : null,
441
435
  'Apply these world rules symmetrically when responding in this conversation.',
442
436
  ].filter(Boolean);
@@ -9,7 +9,6 @@ import { registerOnboardingRoutes } from './onboarding/onboarding-routes.js';
9
9
  import { createMembershipService } from './membership/membership-service.js';
10
10
  import { createMatchmakingService } from './matching/matchmaking-service.js';
11
11
  import { createWorldSearchService } from './search/search-service.js';
12
- import { createResultService } from './results/result-service.js';
13
12
  import { createWorldConversationOrchestrator } from './orchestration/world-conversation-orchestrator.js';
14
13
  import { createSocialService } from './social/social-service.js';
15
14
  import { registerSocialRoutes } from './social/social-routes.js';
@@ -50,7 +49,6 @@ export function createClaworldProductShell({
50
49
  chatRequestService,
51
50
  store,
52
51
  });
53
- const resultService = createResultService();
54
52
  const friendService = createFriendService({ store, policy: relay?.policy });
55
53
  const socialLookupService = createSocialService({ worldService, store });
56
54
  const feedbackService = createFeedbackService({ store });
@@ -62,7 +60,6 @@ export function createClaworldProductShell({
62
60
  };
63
61
  const worldConversationOrchestrator = createWorldConversationOrchestrator({
64
62
  worldService,
65
- resultService,
66
63
  });
67
64
 
68
65
  const productShell = {
@@ -78,7 +75,6 @@ export function createClaworldProductShell({
78
75
  chatRequests: chatRequestService,
79
76
  moderation: worldAdminService,
80
77
  feedback: feedbackService,
81
- results: resultService,
82
78
  orchestration: worldConversationOrchestrator,
83
79
  },
84
80
  registerRoutes(app) {
@@ -115,7 +111,6 @@ export function createClaworldProductShell({
115
111
  'moderation',
116
112
  'feedback',
117
113
  'orchestration',
118
- 'results',
119
114
  ],
120
115
  routes: [
121
116
  'GET /v1/meta/product-shell',
@@ -132,6 +127,7 @@ export function createClaworldProductShell({
132
127
  'GET /v1/chat-requests',
133
128
  'PUT /v1/chat-requests/approval-policy',
134
129
  'POST /v1/chat-requests/:chatRequestId/accept',
130
+ 'POST /v1/chat-requests/:chatRequestId/reject',
135
131
  'GET /v1/worlds',
136
132
  'GET /v1/worlds/:worldId',
137
133
  'POST /v1/worlds',
@@ -81,9 +81,13 @@ export function createMembershipService({ worldService, store = null } = {}) {
81
81
  }
82
82
 
83
83
  return {
84
- evaluateJoin({ worldId, participantContextText = null } = {}) {
84
+ evaluateJoin({ worldId, participantContextText = null, profile = null, profileSnapshot = null } = {}) {
85
85
  const world = worldService.requireWorld(worldId);
86
- const normalizedParticipantContextText = normalizeText(participantContextText, null);
86
+ const normalizedParticipantContextText = resolveNormalizedParticipantContextText({
87
+ world,
88
+ participantContextText,
89
+ profileSnapshot: profileSnapshot || profile,
90
+ });
87
91
  const accepted = Boolean(normalizedParticipantContextText);
88
92
 
89
93
  return {
@@ -132,7 +136,7 @@ export function createMembershipService({ worldService, store = null } = {}) {
132
136
  };
133
137
  },
134
138
 
135
- async createMembership({ worldId, agentId, participantContextText } = {}) {
139
+ async createMembership({ worldId, agentId, participantContextText, profile = null, profileSnapshot = null } = {}) {
136
140
  const world = worldService.requireWorld(worldId);
137
141
  const membershipStore = assertStore();
138
142
  const normalizedAgentId = normalizeAgentId(agentId);
@@ -145,6 +149,7 @@ export function createMembershipService({ worldService, store = null } = {}) {
145
149
  world,
146
150
  agent,
147
151
  participantContextText,
152
+ profileSnapshot: profileSnapshot || profile,
148
153
  });
149
154
  if (!normalizedParticipantContextText) {
150
155
  throw createInvalidJoinRequestError(
@@ -166,7 +171,7 @@ export function createMembershipService({ worldService, store = null } = {}) {
166
171
  worldId,
167
172
  agentId: normalizedAgentId,
168
173
  status: 'joined',
169
- profileSnapshot: normalizeProfileSnapshot(null, normalizedParticipantContextText),
174
+ profileSnapshot: normalizeProfileSnapshot(profileSnapshot || profile, normalizedParticipantContextText),
170
175
  participantContextText: normalizedParticipantContextText,
171
176
  });
172
177
 
@@ -177,6 +182,8 @@ export function createMembershipService({ worldService, store = null } = {}) {
177
182
  worldId,
178
183
  agentId,
179
184
  participantContextText,
185
+ profile = null,
186
+ profileSnapshot = null,
180
187
  } = {}) {
181
188
  const world = worldService.requireWorld(worldId);
182
189
  const membershipStore = assertStore();
@@ -191,6 +198,7 @@ export function createMembershipService({ worldService, store = null } = {}) {
191
198
  world,
192
199
  agent,
193
200
  participantContextText,
201
+ profileSnapshot: profileSnapshot || profile,
194
202
  });
195
203
  if (!normalizedParticipantContextText) {
196
204
  throw createInvalidJoinRequestError(
@@ -207,14 +215,24 @@ export function createMembershipService({ worldService, store = null } = {}) {
207
215
  const membership = existingMembership
208
216
  ? await membershipStore.updateMembership(existingMembership.membershipId, {
209
217
  status: 'active',
210
- profileSnapshot: normalizeProfileSnapshot(existingMembership.profileSnapshot, normalizedParticipantContextText),
218
+ profileSnapshot: normalizeProfileSnapshot(
219
+ {
220
+ ...(existingMembership.profileSnapshot && typeof existingMembership.profileSnapshot === 'object'
221
+ ? existingMembership.profileSnapshot
222
+ : {}),
223
+ ...((profileSnapshot || profile) && typeof (profileSnapshot || profile) === 'object' && !Array.isArray(profileSnapshot || profile)
224
+ ? (profileSnapshot || profile)
225
+ : {}),
226
+ },
227
+ normalizedParticipantContextText,
228
+ ),
211
229
  participantContextText: normalizedParticipantContextText,
212
230
  })
213
231
  : await membershipStore.createMembership({
214
232
  worldId: world.worldId,
215
233
  agentId: normalizedAgentId,
216
234
  status: 'active',
217
- profileSnapshot: normalizeProfileSnapshot(null, normalizedParticipantContextText),
235
+ profileSnapshot: normalizeProfileSnapshot(profileSnapshot || profile, normalizedParticipantContextText),
218
236
  participantContextText: normalizedParticipantContextText,
219
237
  });
220
238
 
@@ -2,7 +2,6 @@ import { createSystemMessageOrchestrator } from './world-conversation-text.js';
2
2
 
3
3
  export function createWorldConversationOrchestrator({
4
4
  worldService,
5
- resultService,
6
5
  systemMessages = createSystemMessageOrchestrator(),
7
6
  } = {}) {
8
7
  return {
@@ -22,7 +21,6 @@ export function createWorldConversationOrchestrator({
22
21
  },
23
22
  openingPlan,
24
23
  convergencePlan,
25
- resultPreview: resultService.previewConversation({ world, conversationKey }),
26
24
  status: 'preview_ready',
27
25
  };
28
26
  },
@@ -86,7 +86,6 @@ export function buildWorldConversationContextEvent(detail = {}) {
86
86
  const convergenceText = normalizeText(conversationOverview.convergence?.text, null);
87
87
  const interactionRules = normalizeText(detail.interactionRules, null);
88
88
  const prohibitedRules = normalizeText(detail.prohibitedRules, null);
89
- const ratingRules = normalizeText(detail.ratingRules, null);
90
89
 
91
90
  const lines = [
92
91
  'Internal Claworld world context for this conversation.',
@@ -98,7 +97,6 @@ export function buildWorldConversationContextEvent(detail = {}) {
98
97
  openingText ? `Opening focus: ${openingText}` : null,
99
98
  interactionRules ? `Interaction rules: ${interactionRules}` : null,
100
99
  prohibitedRules ? `Prohibited rules: ${prohibitedRules}` : null,
101
- ratingRules ? `Rating rules: ${ratingRules}` : null,
102
100
  convergenceText ? `Convergence rule: ${convergenceText}` : null,
103
101
  'Apply these world rules symmetrically when responding in this conversation.',
104
102
  ].filter(Boolean);
@@ -56,7 +56,10 @@ export function registerChatRequestRoutes(app, { chatRequestService, store }) {
56
56
  targetAgentId: req.body?.targetAgentId,
57
57
  kickoffBrief: req.body?.kickoffBrief,
58
58
  openingMessage: req.body?.openingMessage,
59
+ openingPayload: req.body?.openingPayload,
59
60
  worldId: req.body?.worldId,
61
+ requestContext: req.body?.requestContext,
62
+ source: req.body?.source,
60
63
  });
61
64
  res.status(201).json(result);
62
65
  } catch (error) {
@@ -75,7 +78,7 @@ export function registerChatRequestRoutes(app, { chatRequestService, store }) {
75
78
  if (!authAgent.agentId) return sendMissingAgentIdentity(res);
76
79
 
77
80
  try {
78
- const result = chatRequestService.listChatRequests({
81
+ const result = chatRequestService.listChatInbox({
79
82
  agentId: authAgent.agentId,
80
83
  direction: req.query.direction,
81
84
  });
@@ -104,4 +107,24 @@ export function registerChatRequestRoutes(app, { chatRequestService, store }) {
104
107
  sendChatRequestError(res, error);
105
108
  }
106
109
  });
110
+
111
+ app.post('/v1/chat-requests/:chatRequestId/reject', async (req, res) => {
112
+ const authAgent = resolveAuthenticatedAgentId({
113
+ store,
114
+ req,
115
+ providedAgentId: req.body?.actorAgentId,
116
+ fieldName: 'actorAgentId',
117
+ });
118
+ if (!authAgent.ok) return res.status(authAgent.status).json(authAgent.body);
119
+ if (!authAgent.agentId) return sendMissingAgentIdentity(res);
120
+
121
+ try {
122
+ const result = await chatRequestService.rejectChatRequest(req.params.chatRequestId, {
123
+ actorAgentId: authAgent.agentId,
124
+ });
125
+ res.json(result);
126
+ } catch (error) {
127
+ sendChatRequestError(res, error);
128
+ }
129
+ });
107
130
  }