@xfxstudio/claworld 0.2.6 → 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.
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/skills/claworld-help/SKILL.md +2 -2
- package/skills/claworld-join-and-chat/SKILL.md +18 -9
- package/src/lib/chat-request.js +19 -0
- package/src/lib/relay/kickoff-text.js +6 -1
- package/src/openclaw/installer/core.js +16 -2
- package/src/openclaw/plugin/claworld-channel-plugin.js +164 -12
- package/src/openclaw/plugin/config-schema.js +9 -4
- package/src/openclaw/plugin/register.js +38 -18
- package/src/openclaw/plugin/relay-client.js +502 -1
- package/src/openclaw/runtime/demo-session-bootstrap.js +1 -2
- package/src/openclaw/runtime/tool-contracts.js +39 -0
- package/src/openclaw/runtime/tool-inventory.js +1 -1
- package/src/openclaw/runtime/world-moderation-helper.js +8 -12
- package/src/product-shell/catalog/default-world-catalog.js +3 -3
- package/src/product-shell/contracts/world-manifest.js +0 -24
- package/src/product-shell/contracts/world-orchestration.js +0 -4
- package/src/product-shell/index.js +0 -5
- package/src/product-shell/orchestration/world-conversation-orchestrator.js +0 -2
- package/src/product-shell/orchestration/world-conversation-text.js +0 -2
- package/src/product-shell/social/chat-request-routes.js +4 -1
- package/src/product-shell/social/chat-request-service.js +163 -15
- package/src/product-shell/worlds/world-admin-service.js +0 -20
- package/src/product-shell/worlds/world-authorization.js +15 -1
- package/src/product-shell/worlds/world-text.js +0 -2
- package/src/product-shell/results/result-service.js +0 -21
|
@@ -23,8 +23,7 @@ export function createDemoSessionBootstrap() {
|
|
|
23
23
|
'load demo world',
|
|
24
24
|
'create manual session',
|
|
25
25
|
'inject opening system message',
|
|
26
|
-
'wait for
|
|
27
|
-
'build canonical result',
|
|
26
|
+
'wait for conversation closure or manual stop',
|
|
28
27
|
],
|
|
29
28
|
status: 'planned',
|
|
30
29
|
};
|
|
@@ -622,6 +622,22 @@ function projectChatRequestItem(request = {}) {
|
|
|
622
622
|
};
|
|
623
623
|
}
|
|
624
624
|
|
|
625
|
+
function projectChatInboxChatItem(chat = {}) {
|
|
626
|
+
if (!chat || typeof chat !== 'object' || Array.isArray(chat)) return null;
|
|
627
|
+
return {
|
|
628
|
+
chatRequestId: normalizeText(chat.chatRequestId, null),
|
|
629
|
+
status: normalizeText(chat.status, null),
|
|
630
|
+
direction: normalizeText(chat.direction, null),
|
|
631
|
+
createdAt: normalizeText(chat.createdAt, null),
|
|
632
|
+
updatedAt: normalizeText(chat.updatedAt, null),
|
|
633
|
+
lastTurnAt: normalizeText(chat.lastTurnAt, null),
|
|
634
|
+
conversationKey: normalizeText(chat.conversationKey, null),
|
|
635
|
+
localSessionKey: normalizeText(chat.localSessionKey, normalizeText(chat.sessionKey, null)),
|
|
636
|
+
counterparty: projectToolAgentSummary(chat.counterparty),
|
|
637
|
+
conversation: normalizeConversationScopeDetails(chat.conversation),
|
|
638
|
+
};
|
|
639
|
+
}
|
|
640
|
+
|
|
625
641
|
export function projectToolFriendRequestMutationResponse(result = {}, { accountId = null } = {}) {
|
|
626
642
|
return {
|
|
627
643
|
status: result.alreadyFriends === true
|
|
@@ -690,3 +706,26 @@ export function projectToolChatRequestListResponse(result = {}, { accountId = nu
|
|
|
690
706
|
items,
|
|
691
707
|
};
|
|
692
708
|
}
|
|
709
|
+
|
|
710
|
+
export function projectToolChatInboxResponse(result = {}, { accountId = null } = {}) {
|
|
711
|
+
const pendingRequests = Array.isArray(result.pendingRequests)
|
|
712
|
+
? result.pendingRequests.map((request) => projectChatRequestItem(request)).filter(Boolean)
|
|
713
|
+
: [];
|
|
714
|
+
const chats = Array.isArray(result.chats)
|
|
715
|
+
? result.chats.map((chat) => projectChatInboxChatItem(chat)).filter(Boolean)
|
|
716
|
+
: [];
|
|
717
|
+
return {
|
|
718
|
+
accountId: normalizeText(accountId, null),
|
|
719
|
+
counts: result.counts && typeof result.counts === 'object' && !Array.isArray(result.counts)
|
|
720
|
+
? {
|
|
721
|
+
pendingRequestCount: normalizeInteger(result.counts.pendingRequestCount, pendingRequests.length),
|
|
722
|
+
chatCount: normalizeInteger(result.counts.chatCount, chats.length),
|
|
723
|
+
}
|
|
724
|
+
: {
|
|
725
|
+
pendingRequestCount: pendingRequests.length,
|
|
726
|
+
chatCount: chats.length,
|
|
727
|
+
},
|
|
728
|
+
pendingRequests,
|
|
729
|
+
chats,
|
|
730
|
+
};
|
|
731
|
+
}
|
|
@@ -207,12 +207,11 @@ export async function createModeratedWorld({
|
|
|
207
207
|
const baseUrl = normalizeRelayHttpBaseUrl(resolvedRuntimeConfig.serverUrl);
|
|
208
208
|
const created = await fetchJson(fetchImpl, `${baseUrl}/v1/worlds`, {
|
|
209
209
|
method: 'POST',
|
|
210
|
-
headers: {
|
|
210
|
+
headers: buildRuntimeAuthHeaders(resolvedRuntimeConfig, {
|
|
211
211
|
accept: 'application/json',
|
|
212
212
|
'content-type': 'application/json',
|
|
213
213
|
...(resolvedRuntimeConfig.apiKey ? { 'x-api-key': resolvedRuntimeConfig.apiKey } : {}),
|
|
214
|
-
|
|
215
|
-
},
|
|
214
|
+
}),
|
|
216
215
|
body: JSON.stringify({
|
|
217
216
|
agentId: resolvedAgentId,
|
|
218
217
|
displayName,
|
|
@@ -259,11 +258,10 @@ export async function fetchOwnedWorlds({
|
|
|
259
258
|
requestUrl.searchParams.set('agentId', resolvedAgentId);
|
|
260
259
|
requestUrl.searchParams.set('includeDisabled', includeDisabled ? 'true' : 'false');
|
|
261
260
|
const result = await fetchJson(fetchImpl, requestUrl.toString(), {
|
|
262
|
-
headers: {
|
|
261
|
+
headers: buildRuntimeAuthHeaders(resolvedRuntimeConfig, {
|
|
263
262
|
accept: 'application/json',
|
|
264
263
|
...(resolvedRuntimeConfig.apiKey ? { 'x-api-key': resolvedRuntimeConfig.apiKey } : {}),
|
|
265
|
-
|
|
266
|
-
},
|
|
264
|
+
}),
|
|
267
265
|
});
|
|
268
266
|
|
|
269
267
|
if (!result.ok) {
|
|
@@ -312,11 +310,10 @@ export async function manageModeratedWorld({
|
|
|
312
310
|
const requestUrl = new URL(`${baseUrl}/v1/moderation/worlds/${encodeURIComponent(resolvedWorldId)}`);
|
|
313
311
|
requestUrl.searchParams.set('agentId', resolvedAgentId);
|
|
314
312
|
const result = await fetchJson(fetchImpl, requestUrl.toString(), {
|
|
315
|
-
headers: {
|
|
313
|
+
headers: buildRuntimeAuthHeaders(resolvedRuntimeConfig, {
|
|
316
314
|
accept: 'application/json',
|
|
317
315
|
...(resolvedRuntimeConfig.apiKey ? { 'x-api-key': resolvedRuntimeConfig.apiKey } : {}),
|
|
318
|
-
|
|
319
|
-
},
|
|
316
|
+
}),
|
|
320
317
|
});
|
|
321
318
|
|
|
322
319
|
if (!result.ok) {
|
|
@@ -334,12 +331,11 @@ export async function manageModeratedWorld({
|
|
|
334
331
|
|
|
335
332
|
const result = await fetchJson(fetchImpl, `${baseUrl}/v1/moderation/worlds/${encodeURIComponent(resolvedWorldId)}`, {
|
|
336
333
|
method: 'PATCH',
|
|
337
|
-
headers: {
|
|
334
|
+
headers: buildRuntimeAuthHeaders(resolvedRuntimeConfig, {
|
|
338
335
|
accept: 'application/json',
|
|
339
336
|
'content-type': 'application/json',
|
|
340
337
|
...(resolvedRuntimeConfig.apiKey ? { 'x-api-key': resolvedRuntimeConfig.apiKey } : {}),
|
|
341
|
-
|
|
342
|
-
},
|
|
338
|
+
}),
|
|
343
339
|
body: JSON.stringify({
|
|
344
340
|
agentId: resolvedAgentId,
|
|
345
341
|
...(changes && typeof changes === 'object' ? { changes } : {}),
|
|
@@ -9,7 +9,7 @@ export const DEFAULT_WORLD_MANIFESTS = Object.freeze([
|
|
|
9
9
|
lifecycle: 'prototype',
|
|
10
10
|
tags: ['dating', 'matching', 'a2a'],
|
|
11
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
|
|
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.',
|
|
13
13
|
},
|
|
14
14
|
{
|
|
15
15
|
worldId: 'skill-handoff-world',
|
|
@@ -21,7 +21,7 @@ export const DEFAULT_WORLD_MANIFESTS = Object.freeze([
|
|
|
21
21
|
lifecycle: 'prototype',
|
|
22
22
|
tags: ['skills', 'services', 'matching'],
|
|
23
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
|
|
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.',
|
|
25
25
|
},
|
|
26
26
|
{
|
|
27
27
|
worldId: 'job-match-world',
|
|
@@ -33,6 +33,6 @@ export const DEFAULT_WORLD_MANIFESTS = Object.freeze([
|
|
|
33
33
|
lifecycle: 'prototype',
|
|
34
34
|
tags: ['jobs', 'recruiting', 'screening'],
|
|
35
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
|
|
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.',
|
|
37
37
|
},
|
|
38
38
|
]);
|
|
@@ -77,15 +77,6 @@ function normalizeField(field = {}, index = 0, { required = false } = {}) {
|
|
|
77
77
|
};
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
-
function normalizeExampleSignals(exampleSignals = {}) {
|
|
81
|
-
const normalizeGroup = (key) => (Array.isArray(exampleSignals[key]) ? exampleSignals[key] : []);
|
|
82
|
-
return {
|
|
83
|
-
intentSignals: normalizeGroup('intentSignals'),
|
|
84
|
-
conversationSignals: normalizeGroup('conversationSignals'),
|
|
85
|
-
agentSignals: normalizeGroup('agentSignals'),
|
|
86
|
-
};
|
|
87
|
-
}
|
|
88
|
-
|
|
89
80
|
function normalizeJoinSchema(joinSchema = {}) {
|
|
90
81
|
return {
|
|
91
82
|
requiredFields: [
|
|
@@ -167,10 +158,6 @@ export function normalizeWorldManifest(manifest = {}, index = 0) {
|
|
|
167
158
|
const defaultInteractionRules = normalizeText(conversationTemplate?.worldRules?.openingText, null);
|
|
168
159
|
const normalizedInteractionRules = normalizeText(manifest.interactionRules, defaultInteractionRules);
|
|
169
160
|
const normalizedProhibitedRules = normalizeText(manifest.prohibitedRules, null);
|
|
170
|
-
const normalizedRatingRules = normalizeText(
|
|
171
|
-
manifest.ratingRules,
|
|
172
|
-
'After the interaction ends, each agent should rate the other participant from 1 to 10 based on fit, clarity, and rule compliance.',
|
|
173
|
-
);
|
|
174
161
|
|
|
175
162
|
return {
|
|
176
163
|
worldId,
|
|
@@ -183,7 +170,6 @@ export function normalizeWorldManifest(manifest = {}, index = 0) {
|
|
|
183
170
|
tags: normalizeStringList(manifest.tags),
|
|
184
171
|
interactionRules: normalizedInteractionRules,
|
|
185
172
|
prohibitedRules: normalizedProhibitedRules,
|
|
186
|
-
ratingRules: normalizedRatingRules,
|
|
187
173
|
worldContextText: buildWorldContextText({
|
|
188
174
|
worldId,
|
|
189
175
|
displayName: normalizeText(manifest.displayName, worldId),
|
|
@@ -194,7 +180,6 @@ export function normalizeWorldManifest(manifest = {}, index = 0) {
|
|
|
194
180
|
),
|
|
195
181
|
interactionRules: normalizedInteractionRules,
|
|
196
182
|
prohibitedRules: normalizedProhibitedRules,
|
|
197
|
-
ratingRules: normalizedRatingRules,
|
|
198
183
|
}),
|
|
199
184
|
creatorAgentId: normalizeText(manifest.creatorAgentId, null),
|
|
200
185
|
eligibility: normalizeWorldEligibility(manifest.eligibility, 'active'),
|
|
@@ -232,12 +217,6 @@ export function normalizeWorldManifest(manifest = {}, index = 0) {
|
|
|
232
217
|
stateChangeMessages: conversationTemplate?.worldRules?.stateChangeMessages || {},
|
|
233
218
|
},
|
|
234
219
|
},
|
|
235
|
-
resultContract: {
|
|
236
|
-
schemaId: normalizeText(manifest.resultContract?.schemaId, `${worldId}.result.v1`),
|
|
237
|
-
outputs: normalizeStringList(manifest.resultContract?.outputs),
|
|
238
|
-
successCriteria: normalizeStringList(manifest.resultContract?.successCriteria),
|
|
239
|
-
exampleSignals: normalizeExampleSignals(manifest.resultContract?.exampleSignals),
|
|
240
|
-
},
|
|
241
220
|
meta: {
|
|
242
221
|
status: normalizeText(manifest.meta?.status, 'scaffold_ready'),
|
|
243
222
|
persistence: normalizeText(manifest.meta?.persistence, 'in_memory_catalog'),
|
|
@@ -311,7 +290,6 @@ export function projectWorldDetail(world) {
|
|
|
311
290
|
summary: world.summary,
|
|
312
291
|
interactionRules: world.interactionRules,
|
|
313
292
|
prohibitedRules: world.prohibitedRules,
|
|
314
|
-
ratingRules: world.ratingRules,
|
|
315
293
|
conversationMode: world.conversationTemplate.mode,
|
|
316
294
|
conversationOverview: {
|
|
317
295
|
worldId: world.worldId,
|
|
@@ -359,7 +337,6 @@ export function projectConversationWorldContext(world) {
|
|
|
359
337
|
summary: world.summary,
|
|
360
338
|
interactionRules: world.interactionRules,
|
|
361
339
|
prohibitedRules: world.prohibitedRules,
|
|
362
|
-
ratingRules: world.ratingRules,
|
|
363
340
|
conversationMode: world.conversationTemplate.mode,
|
|
364
341
|
conversationOverview: {
|
|
365
342
|
worldId: world.worldId,
|
|
@@ -378,7 +355,6 @@ export function projectConversationWorldContext(world) {
|
|
|
378
355
|
worldContextText: runtimeWorldContext.text,
|
|
379
356
|
interactionRules: world.interactionRules,
|
|
380
357
|
prohibitedRules: world.prohibitedRules,
|
|
381
|
-
ratingRules: world.ratingRules,
|
|
382
358
|
conversationMode: world.conversationTemplate.mode,
|
|
383
359
|
conversationOverview: {
|
|
384
360
|
worldId: world.worldId,
|
|
@@ -173,7 +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
176
|
eligibility: normalizeText(payload.eligibility, 'active'),
|
|
178
177
|
broadcast: normalizeBroadcastConfig(payload.broadcast),
|
|
179
178
|
requiredFields,
|
|
@@ -235,7 +234,6 @@ function normalizeWorldDetail(payload = {}) {
|
|
|
235
234
|
),
|
|
236
235
|
interactionRules: normalizeText(payload.interactionRules || world.interactionRules, null),
|
|
237
236
|
prohibitedRules: normalizeText(payload.prohibitedRules || world.prohibitedRules, null),
|
|
238
|
-
ratingRules: normalizeText(payload.ratingRules || world.ratingRules, null),
|
|
239
237
|
eligibility: normalizeText(payload.eligibility || world.eligibility, 'active'),
|
|
240
238
|
broadcast: normalizeBroadcastConfig(payload.broadcast || world.broadcast),
|
|
241
239
|
requiredFields,
|
|
@@ -422,7 +420,6 @@ export function buildWorldSessionStartupText(detail = {}) {
|
|
|
422
420
|
const convergenceText = normalizeText(conversationOverview.convergence?.text, null);
|
|
423
421
|
const interactionRules = normalizeText(normalizedDetail.interactionRules, null);
|
|
424
422
|
const prohibitedRules = normalizeText(normalizedDetail.prohibitedRules, null);
|
|
425
|
-
const ratingRules = normalizeText(normalizedDetail.ratingRules, null);
|
|
426
423
|
|
|
427
424
|
const lines = [
|
|
428
425
|
'Internal Claworld world context for this conversation.',
|
|
@@ -434,7 +431,6 @@ export function buildWorldSessionStartupText(detail = {}) {
|
|
|
434
431
|
openingText ? `Opening focus: ${openingText}` : null,
|
|
435
432
|
interactionRules ? `Interaction rules: ${interactionRules}` : null,
|
|
436
433
|
prohibitedRules ? `Prohibited rules: ${prohibitedRules}` : null,
|
|
437
|
-
ratingRules ? `Rating rules: ${ratingRules}` : null,
|
|
438
434
|
convergenceText ? `Convergence rule: ${convergenceText}` : null,
|
|
439
435
|
'Apply these world rules symmetrically when responding in this conversation.',
|
|
440
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',
|
|
@@ -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.
|
|
81
|
+
const result = chatRequestService.listChatInbox({
|
|
79
82
|
agentId: authAgent.agentId,
|
|
80
83
|
direction: req.query.direction,
|
|
81
84
|
});
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { normalizeAgentProfile, resolveAgentDisplayName, resolveAgentVisibility } from '../../lib/agent-profile.js';
|
|
2
|
+
import { normalizeChatRequestInput } from '../../lib/chat-request.js';
|
|
2
3
|
import { createKickoffBrief, resolveStoredKickoffBrief } from '../../lib/relay/kickoff-text.js';
|
|
3
4
|
import { WORLD_ACTIONS } from '../worlds/world-authorization.js';
|
|
4
5
|
import { normalizeChatRequestApprovalPolicy } from '../contracts/chat-request-approval-policy.js';
|
|
@@ -198,6 +199,7 @@ function resolveAcceptNextAction(kickoff) {
|
|
|
198
199
|
const kickoffStatus = kickoff?.status || 'skipped';
|
|
199
200
|
if (kickoffStatus === 'established') return 'runtime_owns_live_conversation';
|
|
200
201
|
if (kickoffStatus === 'sent') return 'wait_for_sender_opener_delivery';
|
|
202
|
+
if (kickoffStatus === 'kept_silent') return 'sender_kept_silent';
|
|
201
203
|
if (kickoffStatus === 'failed') return 'backend_kickoff_failed';
|
|
202
204
|
return 'chat_request_accepted_without_opening_message';
|
|
203
205
|
}
|
|
@@ -280,6 +282,92 @@ function projectChatRequestOrigin(request = {}) {
|
|
|
280
282
|
};
|
|
281
283
|
}
|
|
282
284
|
|
|
285
|
+
function hasConversationScope(conversation = null) {
|
|
286
|
+
return Boolean(conversation && typeof conversation === 'object' && !Array.isArray(conversation) && Object.keys(conversation).length > 0);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
function resolveConversationLinkRequest(requests = [], conversation = {}) {
|
|
290
|
+
const conversationMeta = conversation?.meta && typeof conversation.meta === 'object' && !Array.isArray(conversation.meta)
|
|
291
|
+
? conversation.meta
|
|
292
|
+
: {};
|
|
293
|
+
const approvalRequestId = normalizeText(conversationMeta.approvalRequestId, null);
|
|
294
|
+
if (approvalRequestId) {
|
|
295
|
+
return requests.find((request) => normalizeText(request.chatRequestId || request.requestId, null) === approvalRequestId) || null;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
const conversationKey = normalizeText(conversation?.conversationKey, null);
|
|
299
|
+
if (!conversationKey) return null;
|
|
300
|
+
|
|
301
|
+
return requests.find((request) => {
|
|
302
|
+
const kickoff = request?.kickoff && typeof request.kickoff === 'object' && !Array.isArray(request.kickoff)
|
|
303
|
+
? request.kickoff
|
|
304
|
+
: {};
|
|
305
|
+
return normalizeText(kickoff.conversationKey, null) === conversationKey
|
|
306
|
+
|| normalizeText(request?.conversation?.conversationKey, null) === conversationKey;
|
|
307
|
+
}) || null;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
function resolveInboxChatDirection(viewerAgentId, request = null) {
|
|
311
|
+
if (!request) return null;
|
|
312
|
+
if (viewerAgentId === request.fromAgentId) return 'outbound';
|
|
313
|
+
if (viewerAgentId === request.toAgentId) return 'inbound';
|
|
314
|
+
return 'related';
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
function resolveInboxChatCounterpartyAgentId(viewerAgentId, request = null, conversation = null) {
|
|
318
|
+
if (request) {
|
|
319
|
+
if (viewerAgentId === request.fromAgentId) return request.toAgentId;
|
|
320
|
+
if (viewerAgentId === request.toAgentId) return request.fromAgentId;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
const participantIds = Array.isArray(conversation?.participantIds) ? conversation.participantIds : [];
|
|
324
|
+
return participantIds.find((participantId) => participantId && participantId !== viewerAgentId) || null;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
function resolveInboxChatStatus(conversation = {}, request = null) {
|
|
328
|
+
const kickoffStatus = normalizeText(request?.kickoff?.status, null);
|
|
329
|
+
if (kickoffStatus === 'sent') return 'opening';
|
|
330
|
+
if (kickoffStatus === 'kept_silent') return 'silent';
|
|
331
|
+
if (kickoffStatus === 'failed') return 'kickoff_failed';
|
|
332
|
+
const conversationStatus = normalizeText(conversation?.status, 'active');
|
|
333
|
+
if (conversationStatus === 'active') return 'active';
|
|
334
|
+
if (conversationStatus === 'closed') return 'ended';
|
|
335
|
+
return conversationStatus;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
function resolveInboxConversationWorldSummary(worldService, request = {}, conversation = {}) {
|
|
339
|
+
const requestContext = request?.requestContext && typeof request.requestContext === 'object' && !Array.isArray(request.requestContext)
|
|
340
|
+
? request.requestContext
|
|
341
|
+
: {};
|
|
342
|
+
const requestConversation = request?.conversation && typeof request.conversation === 'object' && !Array.isArray(request.conversation)
|
|
343
|
+
? request.conversation
|
|
344
|
+
: {};
|
|
345
|
+
const conversationMeta = conversation?.meta && typeof conversation.meta === 'object' && !Array.isArray(conversation.meta)
|
|
346
|
+
? conversation.meta
|
|
347
|
+
: {};
|
|
348
|
+
const worldMeta = conversationMeta.world && typeof conversationMeta.world === 'object' && !Array.isArray(conversationMeta.world)
|
|
349
|
+
? conversationMeta.world
|
|
350
|
+
: {};
|
|
351
|
+
const worldId = normalizeConversationWorldId(requestConversation.worldId)
|
|
352
|
+
|| normalizeConversationWorldId(requestContext.conversation?.worldId)
|
|
353
|
+
|| normalizeConversationWorldId(worldMeta.worldId)
|
|
354
|
+
|| normalizeConversationWorldId(conversationMeta.worldId);
|
|
355
|
+
|
|
356
|
+
if (!worldId) {
|
|
357
|
+
return {
|
|
358
|
+
mode: 'direct',
|
|
359
|
+
worldId: null,
|
|
360
|
+
world: null,
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
return {
|
|
365
|
+
mode: 'world',
|
|
366
|
+
worldId,
|
|
367
|
+
world: projectWorldSummary(worldService, worldId),
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
|
|
283
371
|
export function createChatRequestService({
|
|
284
372
|
store = null,
|
|
285
373
|
relay = null,
|
|
@@ -349,8 +437,27 @@ export function createChatRequestService({
|
|
|
349
437
|
};
|
|
350
438
|
}
|
|
351
439
|
|
|
440
|
+
function projectChatInboxChat(conversation = {}, viewerAgentId = null, request = null) {
|
|
441
|
+
const direction = resolveInboxChatDirection(viewerAgentId, request);
|
|
442
|
+
const counterpartyAgentId = resolveInboxChatCounterpartyAgentId(viewerAgentId, request, conversation);
|
|
443
|
+
|
|
444
|
+
return {
|
|
445
|
+
chatRequestId: normalizeText(request?.chatRequestId || request?.requestId, null),
|
|
446
|
+
status: resolveInboxChatStatus(conversation, request),
|
|
447
|
+
...(direction ? { direction } : {}),
|
|
448
|
+
createdAt: normalizeText(conversation.createdAt, normalizeText(request?.createdAt, null)),
|
|
449
|
+
updatedAt: normalizeText(conversation.updatedAt, null),
|
|
450
|
+
lastTurnAt: normalizeText(conversation.lastTurnAt, null),
|
|
451
|
+
conversationKey: normalizeText(conversation.conversationKey, null),
|
|
452
|
+
localSessionKey: normalizeText(conversation.sessionKey, null),
|
|
453
|
+
counterparty: projectAgent(assertStore(), counterpartyAgentId, presence),
|
|
454
|
+
conversation: resolveInboxConversationWorldSummary(worldService, request, conversation),
|
|
455
|
+
};
|
|
456
|
+
}
|
|
457
|
+
|
|
352
458
|
return {
|
|
353
459
|
projectChatRequest,
|
|
460
|
+
projectChatInboxChat,
|
|
354
461
|
async syncApprovalPolicy({
|
|
355
462
|
actorAgentId,
|
|
356
463
|
credentialId = null,
|
|
@@ -388,6 +495,7 @@ export function createChatRequestService({
|
|
|
388
495
|
openingMessage = null,
|
|
389
496
|
openingPayload = null,
|
|
390
497
|
worldId = null,
|
|
498
|
+
requestContext = null,
|
|
391
499
|
origin = null,
|
|
392
500
|
broadcast = null,
|
|
393
501
|
source = 'chat_request',
|
|
@@ -398,29 +506,36 @@ export function createChatRequestService({
|
|
|
398
506
|
throw createInvalidChatRequestError('chat_request_target_required', 'chat request target requires targetAgentId');
|
|
399
507
|
}
|
|
400
508
|
const targetAgent = requireAgent(normalizedTargetAgentId);
|
|
401
|
-
const
|
|
509
|
+
const normalizedSource = normalizeText(source, 'chat_request');
|
|
510
|
+
const normalizedRequestInput = normalizeChatRequestInput({
|
|
511
|
+
requestContext,
|
|
512
|
+
source: normalizedSource,
|
|
513
|
+
});
|
|
514
|
+
const normalizedWorldId = normalizeConversationWorldId(worldId)
|
|
515
|
+
|| normalizeConversationWorldId(normalizedRequestInput.conversation?.worldId);
|
|
402
516
|
if (!targetAgent?.address) {
|
|
403
517
|
throw createTargetAddressNotFoundError(normalizedTargetAgentId);
|
|
404
518
|
}
|
|
405
|
-
const normalizedOpeningPayload = cloneJsonObject(openingPayload);
|
|
519
|
+
const normalizedOpeningPayload = cloneJsonObject(openingPayload) || cloneJsonObject(normalizedRequestInput.openingPayload);
|
|
406
520
|
const normalizedKickoffBrief = createKickoffBrief({
|
|
407
521
|
text: typeof kickoffBrief === 'object' && kickoffBrief !== null && !Array.isArray(kickoffBrief)
|
|
408
522
|
? kickoffBrief.text
|
|
409
|
-
: openingMessage,
|
|
523
|
+
: normalizeText(openingMessage, normalizeText(normalizedRequestInput.openingMessage, null)),
|
|
410
524
|
payload: typeof kickoffBrief === 'object' && kickoffBrief !== null && !Array.isArray(kickoffBrief)
|
|
411
525
|
? kickoffBrief.payload
|
|
412
526
|
: normalizedOpeningPayload,
|
|
413
527
|
source: typeof kickoffBrief === 'object' && kickoffBrief !== null && !Array.isArray(kickoffBrief)
|
|
414
528
|
? kickoffBrief.source
|
|
415
|
-
:
|
|
529
|
+
: normalizedSource === 'world_broadcast'
|
|
416
530
|
? 'world_broadcast_brief'
|
|
417
531
|
: 'chat_request_brief',
|
|
418
532
|
});
|
|
419
|
-
const normalizedOrigin = normalizeChatRequestOrigin(origin);
|
|
420
|
-
const normalizedBroadcast = normalizeChatRequestBroadcastMetadata(broadcast)
|
|
533
|
+
const normalizedOrigin = normalizeChatRequestOrigin(origin) || normalizeChatRequestOrigin(normalizedRequestInput.origin);
|
|
534
|
+
const normalizedBroadcast = normalizeChatRequestBroadcastMetadata(broadcast)
|
|
535
|
+
|| normalizeChatRequestBroadcastMetadata(normalizedRequestInput.broadcast);
|
|
421
536
|
if (normalizedWorldId) {
|
|
422
537
|
worldService?.requireWorld?.(normalizedWorldId);
|
|
423
|
-
if (
|
|
538
|
+
if (normalizedSource !== 'world_broadcast') {
|
|
424
539
|
const authorization = worldAuthorizationService.evaluateWorldAction({
|
|
425
540
|
worldId: normalizedWorldId,
|
|
426
541
|
actorAgentId: fromAgentId,
|
|
@@ -444,9 +559,10 @@ export function createChatRequestService({
|
|
|
444
559
|
conversation: {
|
|
445
560
|
...(normalizedWorldId ? { worldId: normalizedWorldId } : {}),
|
|
446
561
|
},
|
|
562
|
+
requestContext: cloneJsonObject(requestContext),
|
|
447
563
|
origin: normalizedOrigin,
|
|
448
564
|
broadcast: normalizedBroadcast,
|
|
449
|
-
source:
|
|
565
|
+
source: normalizedSource,
|
|
450
566
|
});
|
|
451
567
|
if (result.status < 200 || result.status >= 300) {
|
|
452
568
|
throw createRelayResponseError(result, 'chat_request_create_failed');
|
|
@@ -494,23 +610,55 @@ export function createChatRequestService({
|
|
|
494
610
|
};
|
|
495
611
|
},
|
|
496
612
|
|
|
497
|
-
|
|
613
|
+
listChatInbox({ agentId, direction = null } = {}) {
|
|
498
614
|
requireAgent(agentId);
|
|
499
615
|
const normalizedDirection = normalizeDirection(direction);
|
|
500
|
-
let
|
|
501
|
-
|
|
502
|
-
items = items.filter((request) => request.fromAgentId === agentId || request.toAgentId === agentId);
|
|
616
|
+
let requests = assertStore().listChatRequests ? assertStore().listChatRequests() : [];
|
|
617
|
+
requests = requests.filter((request) => request.fromAgentId === agentId || request.toAgentId === agentId);
|
|
503
618
|
if (normalizedDirection === 'inbound') {
|
|
504
|
-
|
|
619
|
+
requests = requests.filter((request) => request.toAgentId === agentId);
|
|
505
620
|
} else if (normalizedDirection === 'outbound') {
|
|
506
|
-
|
|
621
|
+
requests = requests.filter((request) => request.fromAgentId === agentId);
|
|
507
622
|
}
|
|
508
623
|
|
|
624
|
+
const pendingRequests = sortByRecency(
|
|
625
|
+
requests.filter((request) => request.status === 'pending'),
|
|
626
|
+
'createdAt',
|
|
627
|
+
).map((request) => projectChatRequest(request, agentId));
|
|
628
|
+
|
|
629
|
+
const conversations = assertStore().listConversations
|
|
630
|
+
? assertStore().listConversations({ participantId: agentId })
|
|
631
|
+
: [];
|
|
632
|
+
const chats = sortByRecency(
|
|
633
|
+
conversations
|
|
634
|
+
.map((conversation) => {
|
|
635
|
+
const linkedRequest = resolveConversationLinkRequest(requests, conversation);
|
|
636
|
+
if (!linkedRequest && !normalizeText(conversation?.meta?.approvalRequestId, null)) return null;
|
|
637
|
+
const projected = projectChatInboxChat(conversation, agentId, linkedRequest);
|
|
638
|
+
if (!projected) return null;
|
|
639
|
+
if (normalizedDirection && projected.direction !== normalizedDirection) return null;
|
|
640
|
+
return projected;
|
|
641
|
+
})
|
|
642
|
+
.filter(Boolean),
|
|
643
|
+
'lastTurnAt',
|
|
644
|
+
'updatedAt',
|
|
645
|
+
'createdAt',
|
|
646
|
+
);
|
|
647
|
+
|
|
509
648
|
return {
|
|
510
|
-
|
|
649
|
+
counts: {
|
|
650
|
+
pendingRequestCount: pendingRequests.length,
|
|
651
|
+
chatCount: chats.length,
|
|
652
|
+
},
|
|
653
|
+
pendingRequests,
|
|
654
|
+
chats,
|
|
511
655
|
};
|
|
512
656
|
},
|
|
513
657
|
|
|
658
|
+
listChatRequests({ agentId, direction = null } = {}) {
|
|
659
|
+
return this.listChatInbox({ agentId, direction });
|
|
660
|
+
},
|
|
661
|
+
|
|
514
662
|
async acceptChatRequest(chatRequestId, { actorAgentId } = {}) {
|
|
515
663
|
requireAgent(actorAgentId);
|
|
516
664
|
const normalizedChatRequestId = normalizeText(chatRequestId, null);
|