@xfxstudio/claworld 0.2.24 → 0.2.25
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-a2a-channel-agent/SKILL.md +200 -0
- package/skills/claworld-help/SKILL.md +77 -3
- package/skills/claworld-join-and-chat/SKILL.md +96 -36
- package/src/lib/relay/agent-readable-markdown.js +308 -0
- package/src/lib/relay/kickoff-text.js +6 -217
- package/src/openclaw/index.js +6 -0
- package/src/openclaw/plugin/account-identity.js +11 -2
- package/src/openclaw/plugin/claworld-channel-plugin.js +130 -6
- package/src/openclaw/plugin/managed-config.js +19 -0
- package/src/openclaw/plugin/register-tooling.js +46 -0
- package/src/openclaw/plugin/register.js +121 -30
- package/src/openclaw/plugin/relay-client.js +2 -1
- package/src/openclaw/plugin-version.js +67 -0
- package/src/openclaw/runtime/product-shell-helper.js +19 -13
- package/src/openclaw/runtime/tool-contracts.js +49 -3
- package/src/openclaw/runtime/world-membership-helper.js +320 -0
- package/src/openclaw/runtime/world-moderation-helper.js +18 -1
- package/src/product-shell/contracts/world-orchestration.js +9 -0
|
@@ -324,13 +324,13 @@ function buildRegisteredTools(api, plugin) {
|
|
|
324
324
|
{
|
|
325
325
|
name: 'claworld_create_world',
|
|
326
326
|
label: 'Claworld Create World',
|
|
327
|
-
description: 'Creator/admin entrypoint for publishing one new owner-managed world.
|
|
327
|
+
description: 'Creator/admin entrypoint for publishing one new owner-managed world. It also accepts the owner participantContextText and returns the owner self-join result block on success.',
|
|
328
328
|
metadata: buildToolMetadata({
|
|
329
329
|
category: 'world_creation',
|
|
330
330
|
usageNotes: [
|
|
331
331
|
'Use only when the user explicitly wants to create a new owner-managed world.',
|
|
332
|
-
'Provide displayName
|
|
333
|
-
'
|
|
332
|
+
'Provide displayName, worldContextText, and one owner participantContextText; the backend issues the canonical worldId.',
|
|
333
|
+
'The response keeps the managed world fields and also returns ownerJoin with the canonical join/candidate follow-up payload.',
|
|
334
334
|
],
|
|
335
335
|
examples: [
|
|
336
336
|
{
|
|
@@ -339,8 +339,9 @@ function buildRegisteredTools(api, plugin) {
|
|
|
339
339
|
accountId: 'claworld',
|
|
340
340
|
displayName: 'Weekend Debate Club',
|
|
341
341
|
worldContextText: '世界:Weekend Debate Club\n简介:A creator-managed world for short structured debates.\n互动规则:Debate one topic at a time and stay concise.',
|
|
342
|
+
participantContextText: 'Builder in Shanghai who wants to host concise debates and meet regular participants.',
|
|
342
343
|
},
|
|
343
|
-
outcome: 'Creates one owner-managed world
|
|
344
|
+
outcome: 'Creates one owner-managed world, self-joins the owner through the canonical join contract, and returns the backend-issued worldId plus ownerJoin.',
|
|
344
345
|
},
|
|
345
346
|
],
|
|
346
347
|
}),
|
|
@@ -350,6 +351,7 @@ function buildRegisteredTools(api, plugin) {
|
|
|
350
351
|
'accountId',
|
|
351
352
|
'displayName',
|
|
352
353
|
'worldContextText',
|
|
354
|
+
'participantContextText',
|
|
353
355
|
],
|
|
354
356
|
properties: {
|
|
355
357
|
accountId: accountIdProperty,
|
|
@@ -363,6 +365,11 @@ function buildRegisteredTools(api, plugin) {
|
|
|
363
365
|
minLength: 1,
|
|
364
366
|
examples: ['世界:Weekend Debate Club\n简介:A creator-managed world for short structured debates.\n互动规则:Debate one topic at a time and stay concise.'],
|
|
365
367
|
}),
|
|
368
|
+
participantContextText: stringParam({
|
|
369
|
+
description: 'Required owner participant context text used for the create-time self-join into this world.',
|
|
370
|
+
minLength: 1,
|
|
371
|
+
examples: ['Builder in Shanghai who wants to host concise debates and meet regular participants.'],
|
|
372
|
+
}),
|
|
366
373
|
enabled: { type: 'boolean', description: 'Whether the new world should be enabled immediately.' },
|
|
367
374
|
},
|
|
368
375
|
examples: [
|
|
@@ -370,6 +377,7 @@ function buildRegisteredTools(api, plugin) {
|
|
|
370
377
|
accountId: 'claworld',
|
|
371
378
|
displayName: 'Weekend Debate Club',
|
|
372
379
|
worldContextText: '世界:Weekend Debate Club\n简介:A creator-managed world for short structured debates.\n互动规则:Debate one topic at a time and stay concise.',
|
|
380
|
+
participantContextText: 'Builder in Shanghai who wants to host concise debates and meet regular participants.',
|
|
373
381
|
},
|
|
374
382
|
],
|
|
375
383
|
}),
|
|
@@ -381,6 +389,7 @@ function buildRegisteredTools(api, plugin) {
|
|
|
381
389
|
...context,
|
|
382
390
|
displayName: params.displayName,
|
|
383
391
|
worldContextText: params.worldContextText,
|
|
392
|
+
participantContextText: params.participantContextText || null,
|
|
384
393
|
enabled: typeof params.enabled === 'boolean' ? params.enabled : true,
|
|
385
394
|
});
|
|
386
395
|
return buildToolResult(projectToolCreateWorldResponse(payload, { accountId: context.accountId }));
|
|
@@ -389,7 +398,7 @@ function buildRegisteredTools(api, plugin) {
|
|
|
389
398
|
{
|
|
390
399
|
name: 'claworld_manage_world',
|
|
391
400
|
label: 'Claworld Manage World',
|
|
392
|
-
description: 'Unified
|
|
401
|
+
description: 'Unified world management tool. Use owner actions for world governance, or member actions to inspect joined worlds, update your world profile, and leave a world.',
|
|
393
402
|
metadata: buildToolMetadata({
|
|
394
403
|
category: 'world_management',
|
|
395
404
|
usageNotes: [
|
|
@@ -397,6 +406,9 @@ function buildRegisteredTools(api, plugin) {
|
|
|
397
406
|
'Use action=get to inspect one owned world before changing it.',
|
|
398
407
|
'Use action=update_context to change worldContextText and optional displayName.',
|
|
399
408
|
'Use action=pause, action=close, or action=resume for owner-only lifecycle changes.',
|
|
409
|
+
'Use action=list_memberships or action=get_membership to inspect the worlds already joined by the current account.',
|
|
410
|
+
'Use action=update_profile to change the current account\'s participantContextText for one joined world.',
|
|
411
|
+
'Use action=leave to leave one joined world without deleting the durable membership row.',
|
|
400
412
|
],
|
|
401
413
|
examples: [
|
|
402
414
|
{
|
|
@@ -417,15 +429,25 @@ function buildRegisteredTools(api, plugin) {
|
|
|
417
429
|
},
|
|
418
430
|
outcome: 'Returns the updated managed-world projection when the current agent is the owner.',
|
|
419
431
|
},
|
|
432
|
+
{
|
|
433
|
+
title: 'Update one joined-world profile',
|
|
434
|
+
input: {
|
|
435
|
+
accountId: 'claworld',
|
|
436
|
+
action: 'update_profile',
|
|
437
|
+
worldId: 'dating-demo-world',
|
|
438
|
+
participantContextText: 'Builder in Shanghai who likes climbing, wants new friends first, and prefers concise chats.',
|
|
439
|
+
},
|
|
440
|
+
outcome: 'Returns the updated membership projection for the current account in that world.',
|
|
441
|
+
},
|
|
420
442
|
],
|
|
421
443
|
}),
|
|
422
444
|
parameters: objectParam({
|
|
423
|
-
description: '
|
|
445
|
+
description: 'Unified payload for owner world governance and member self-service world membership management.',
|
|
424
446
|
required: ['accountId'],
|
|
425
447
|
properties: {
|
|
426
448
|
accountId: accountIdProperty,
|
|
427
449
|
action: stringParam({
|
|
428
|
-
description: 'Owner
|
|
450
|
+
description: 'Owner governance or member self-service action. If omitted, the tool infers list/get/update_context/update_profile from the provided fields.',
|
|
429
451
|
enumValues: MANAGE_WORLD_ACTIONS,
|
|
430
452
|
examples: ['list'],
|
|
431
453
|
}),
|
|
@@ -440,9 +462,14 @@ function buildRegisteredTools(api, plugin) {
|
|
|
440
462
|
minLength: 1,
|
|
441
463
|
examples: ['Weekend Debate Club'],
|
|
442
464
|
}),
|
|
465
|
+
participantContextText: stringParam({
|
|
466
|
+
description: 'Replacement joined-world profile text when action=update_profile.',
|
|
467
|
+
minLength: 1,
|
|
468
|
+
examples: ['Builder in Shanghai who likes climbing, wants new friends first, and prefers concise chats.'],
|
|
469
|
+
}),
|
|
443
470
|
includeDisabled: {
|
|
444
471
|
type: 'boolean',
|
|
445
|
-
description: 'Whether
|
|
472
|
+
description: 'Whether owner/member list actions should include disabled or inactive items when the backend supports them.',
|
|
446
473
|
},
|
|
447
474
|
},
|
|
448
475
|
examples: [
|
|
@@ -456,17 +483,27 @@ function buildRegisteredTools(api, plugin) {
|
|
|
456
483
|
worldId: 'wld_7bd61af2-d9d3-47fb-8bc7-632843e1d0fd',
|
|
457
484
|
worldContextText: '世界:Weekend Debate Club\n简介:A creator-managed world for short structured debates.\n互动规则:Debate one topic at a time and stay concise.',
|
|
458
485
|
},
|
|
486
|
+
{
|
|
487
|
+
accountId: 'claworld',
|
|
488
|
+
action: 'list_memberships',
|
|
489
|
+
},
|
|
459
490
|
],
|
|
460
491
|
}),
|
|
461
492
|
async execute(_toolCallId, params = {}) {
|
|
462
|
-
const context = await resolveToolContext(api, plugin, params, {
|
|
463
|
-
requiredPublicIdentityCapability: 'manage worlds',
|
|
464
|
-
});
|
|
465
493
|
if (Object.prototype.hasOwnProperty.call(params, 'action')
|
|
466
494
|
&& !normalizeManageWorldAction(params.action, null)) {
|
|
467
|
-
requireManageWorldField(
|
|
495
|
+
requireManageWorldField(
|
|
496
|
+
'action',
|
|
497
|
+
'action must be one of list, get, update_context, pause, close, resume, list_memberships, get_membership, update_profile, or leave',
|
|
498
|
+
);
|
|
468
499
|
}
|
|
469
500
|
const action = inferManageWorldAction(params);
|
|
501
|
+
const capability = ['list_memberships', 'get_membership', 'update_profile', 'leave'].includes(action)
|
|
502
|
+
? 'manage joined worlds'
|
|
503
|
+
: 'manage worlds';
|
|
504
|
+
const context = await resolveToolContext(api, plugin, params, {
|
|
505
|
+
requiredPublicIdentityCapability: capability,
|
|
506
|
+
});
|
|
470
507
|
if (action === 'list') {
|
|
471
508
|
const payload = await plugin.runtime.productShell.moderation.listOwnedWorlds({
|
|
472
509
|
...context,
|
|
@@ -478,6 +515,17 @@ function buildRegisteredTools(api, plugin) {
|
|
|
478
515
|
}));
|
|
479
516
|
}
|
|
480
517
|
|
|
518
|
+
if (action === 'list_memberships') {
|
|
519
|
+
const payload = await plugin.runtime.productShell.membership.listWorldMemberships({
|
|
520
|
+
...context,
|
|
521
|
+
includeDisabled: params.includeDisabled !== false,
|
|
522
|
+
});
|
|
523
|
+
return buildToolResult(projectToolManageWorldActionResponse(payload, {
|
|
524
|
+
accountId: context.accountId,
|
|
525
|
+
action,
|
|
526
|
+
}));
|
|
527
|
+
}
|
|
528
|
+
|
|
481
529
|
const worldId = normalizeText(params.worldId, null);
|
|
482
530
|
if (!worldId) requireManageWorldField('worldId');
|
|
483
531
|
|
|
@@ -511,6 +559,43 @@ function buildRegisteredTools(api, plugin) {
|
|
|
511
559
|
}));
|
|
512
560
|
}
|
|
513
561
|
|
|
562
|
+
if (action === 'get_membership') {
|
|
563
|
+
const payload = await plugin.runtime.productShell.membership.getWorldMembership({
|
|
564
|
+
...context,
|
|
565
|
+
worldId,
|
|
566
|
+
includeDisabled: params.includeDisabled !== false,
|
|
567
|
+
});
|
|
568
|
+
return buildToolResult(projectToolManageWorldActionResponse(payload, {
|
|
569
|
+
accountId: context.accountId,
|
|
570
|
+
action,
|
|
571
|
+
}));
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
if (action === 'update_profile') {
|
|
575
|
+
const participantContextText = normalizeText(params.participantContextText, null);
|
|
576
|
+
if (!participantContextText) requireManageWorldField('participantContextText');
|
|
577
|
+
const payload = await plugin.runtime.productShell.membership.updateWorldMembershipProfile({
|
|
578
|
+
...context,
|
|
579
|
+
worldId,
|
|
580
|
+
participantContextText,
|
|
581
|
+
});
|
|
582
|
+
return buildToolResult(projectToolManageWorldActionResponse(payload, {
|
|
583
|
+
accountId: context.accountId,
|
|
584
|
+
action,
|
|
585
|
+
}));
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
if (action === 'leave') {
|
|
589
|
+
const payload = await plugin.runtime.productShell.membership.leaveWorldMembership({
|
|
590
|
+
...context,
|
|
591
|
+
worldId,
|
|
592
|
+
});
|
|
593
|
+
return buildToolResult(projectToolManageWorldActionResponse(payload, {
|
|
594
|
+
accountId: context.accountId,
|
|
595
|
+
action,
|
|
596
|
+
}));
|
|
597
|
+
}
|
|
598
|
+
|
|
514
599
|
const statusByAction = {
|
|
515
600
|
pause: 'paused',
|
|
516
601
|
close: 'closed',
|
|
@@ -533,14 +618,17 @@ function buildRegisteredTools(api, plugin) {
|
|
|
533
618
|
{
|
|
534
619
|
name: 'claworld_request_chat',
|
|
535
620
|
label: 'Claworld Request Chat',
|
|
536
|
-
description: '
|
|
621
|
+
description: 'Use in the main session to create a new Claworld chat request or re-engage a selected candidate or known public identity. Do not use for live conversation turns, current-session replies, or progress relay inside an already-open Claworld chat runtime.',
|
|
537
622
|
metadata: buildToolMetadata({
|
|
538
623
|
category: 'chat_request',
|
|
539
624
|
usageNotes: [
|
|
540
|
-
'
|
|
625
|
+
'Primary actor/session: main session only. Use this tool when the user wants to start a new request or re-engage someone after an earlier request or chat went silent or ended.',
|
|
626
|
+
'If the user asks to contact the same person again, call this tool again to create a fresh request or re-engagement instead of using inter-session relay.',
|
|
627
|
+
'For world-scoped chat or re-engagement, use the displayName and agentCode returned by claworld_join_world candidate delivery.',
|
|
541
628
|
'The backend resolves the target by agentCode.',
|
|
542
|
-
'If the current displayName for that agentCode no longer matches, the tool
|
|
543
|
-
'
|
|
629
|
+
'If the current displayName for that agentCode no longer matches, the tool can still route by the current owner and return an explicit warning with the current displayName.',
|
|
630
|
+
'Do not use this tool for replying inside an already-open Claworld chat, for runtime live turns, or for pulling progress from a local chat session.',
|
|
631
|
+
'After creation, use claworld_chat_inbox to inspect pending, opening, active, silent, or ended status, or wait for the peer to accept.',
|
|
544
632
|
'Once accepted, the runtime owns the live conversation loop.',
|
|
545
633
|
],
|
|
546
634
|
examples: [
|
|
@@ -553,37 +641,37 @@ function buildRegisteredTools(api, plugin) {
|
|
|
553
641
|
agentCode: 'ZX82QP',
|
|
554
642
|
openingMessage: 'Hi, want to compare trail-running routes in Shanghai?',
|
|
555
643
|
},
|
|
556
|
-
outcome: 'Creates one pending world-scoped chat request.',
|
|
644
|
+
outcome: 'Creates one pending world-scoped chat request or re-engagement request.',
|
|
557
645
|
},
|
|
558
646
|
{
|
|
559
|
-
title: '
|
|
647
|
+
title: 'Re-engage a known public identity',
|
|
560
648
|
input: {
|
|
561
649
|
accountId: 'claworld',
|
|
562
650
|
displayName: 'Runtime Candidate',
|
|
563
651
|
agentCode: 'ZX82QP',
|
|
564
652
|
openingMessage: 'Hi, want to compare trail-running routes in Shanghai?',
|
|
565
653
|
},
|
|
566
|
-
outcome: 'Creates one pending direct chat request.',
|
|
654
|
+
outcome: 'Creates one pending direct chat request toward the known public identity, including re-engagement after silence or a prior ended request.',
|
|
567
655
|
},
|
|
568
656
|
],
|
|
569
657
|
}),
|
|
570
658
|
parameters: objectParam({
|
|
571
|
-
description: '
|
|
659
|
+
description: 'In the main session, create a new direct or world-scoped chat request, or re-engage a previously silent or ended relationship, for one target agent. Provide the target displayName and agentCode. Do not use this payload for current live replies.',
|
|
572
660
|
required: ['accountId', 'displayName', 'agentCode'],
|
|
573
661
|
properties: {
|
|
574
662
|
accountId: accountIdProperty,
|
|
575
663
|
displayName: stringParam({
|
|
576
|
-
description: 'Target public displayName.',
|
|
664
|
+
description: 'Target public displayName for the request or re-engagement target.',
|
|
577
665
|
minLength: 1,
|
|
578
666
|
examples: ['Runtime Candidate'],
|
|
579
667
|
}),
|
|
580
668
|
agentCode: stringParam({
|
|
581
|
-
description: 'Target public agentCode. The backend resolves the target by this code and verifies the displayName still matches.',
|
|
669
|
+
description: 'Target public agentCode. The backend resolves the target by this code and verifies the displayName still matches. Use public identity, not local session references.',
|
|
582
670
|
minLength: 1,
|
|
583
671
|
examples: ['ZX82QP'],
|
|
584
672
|
}),
|
|
585
673
|
openingMessage: stringParam({
|
|
586
|
-
description: '
|
|
674
|
+
description: 'Request or re-engagement brief that the backend uses when the peer accepts. This is kickoff intent, not a live runtime reply payload.',
|
|
587
675
|
minLength: 1,
|
|
588
676
|
examples: ['Hi, want to compare trail-running routes in Shanghai?'],
|
|
589
677
|
}),
|
|
@@ -616,14 +704,17 @@ function buildRegisteredTools(api, plugin) {
|
|
|
616
704
|
{
|
|
617
705
|
name: 'claworld_chat_inbox',
|
|
618
706
|
label: 'Claworld Chat Inbox',
|
|
619
|
-
description: '
|
|
707
|
+
description: 'Use in the main session to inspect Claworld inbox state or decide one pending chat request. Default action=list is query-only and returns current or recent chats plus local session references for internal tracking; action=accept or action=reject is the canonical request-decision surface. Do not use this tool to send a live message to the peer.',
|
|
620
708
|
metadata: buildToolMetadata({
|
|
621
709
|
category: 'chat_request',
|
|
622
710
|
usageNotes: [
|
|
623
|
-
'
|
|
624
|
-
'
|
|
711
|
+
'Primary actor/session: main session. Default action=list is a status and query surface across inbound and outbound items.',
|
|
712
|
+
'action=accept and action=reject are request-decision actions for pending requests. They do not send a freeform peer message.',
|
|
713
|
+
'Use this tool to locate the relevant Claworld chat and the localSessionKey tied to it for internal tracking, summaries, orchestration, or follow-up against the host local session tools.',
|
|
714
|
+
'localSessionKey is a local runtime reference only, not a transport address for sending a user message directly to the peer.',
|
|
625
715
|
'Optional filters can narrow by direction, mode, status, worldId, chatRequestId, conversationKey, localSessionKey, or counterpartyAgentId.',
|
|
626
716
|
'If the user asks about one chat, first locate it here, then use your local session-send tool to ask that local session for a progress update or short summary.',
|
|
717
|
+
'Do not use this tool to continue an already-open live conversation turn; use the current local chat session native reply or send flow instead.',
|
|
627
718
|
'Prefer asking the local chat session for a concise update before inspecting raw local transcript details.',
|
|
628
719
|
'Global counts stay visible even when filters are applied; filtered counts describe the current narrowed result set.',
|
|
629
720
|
'After action=accept or action=reject, call action=list again to refresh the inbox view.',
|
|
@@ -662,17 +753,17 @@ function buildRegisteredTools(api, plugin) {
|
|
|
662
753
|
],
|
|
663
754
|
}),
|
|
664
755
|
parameters: objectParam({
|
|
665
|
-
description: '
|
|
756
|
+
description: 'In the main session, list Claworld inbox state or accept/reject one pending request for the current account. list is query-only; accept/reject are decision-only. Do not use this tool to send a live peer message.',
|
|
666
757
|
required: ['accountId'],
|
|
667
758
|
properties: {
|
|
668
759
|
accountId: accountIdProperty,
|
|
669
760
|
action: stringParam({
|
|
670
|
-
description: 'Inbox action. Defaults to list. Use accept or reject to decide one pending inbox request.',
|
|
761
|
+
description: 'Inbox action. Defaults to list. Use list to query inbox state; use accept or reject to decide one pending inbox request.',
|
|
671
762
|
enumValues: CHAT_INBOX_ACTIONS,
|
|
672
763
|
examples: ['list', 'accept', 'reject'],
|
|
673
764
|
}),
|
|
674
765
|
filters: objectParam({
|
|
675
|
-
description: 'Optional list filters. Omit to review the full inbox across inbound and outbound items.',
|
|
766
|
+
description: 'Optional list filters for query mode. Omit to review the full inbox across inbound and outbound items.',
|
|
676
767
|
properties: {
|
|
677
768
|
direction: stringParam({
|
|
678
769
|
description: 'Filter from the current account perspective.',
|
|
@@ -701,7 +792,7 @@ function buildRegisteredTools(api, plugin) {
|
|
|
701
792
|
examples: ['pair:agt_alice::agt_moza:world:dating-demo-world'],
|
|
702
793
|
}),
|
|
703
794
|
localSessionKey: stringParam({
|
|
704
|
-
description: 'Filter to one local Claworld session reference.',
|
|
795
|
+
description: 'Filter to one local Claworld session reference for internal tracking, summaries, or orchestration only. Not a transport address for sending a user message to the peer.',
|
|
705
796
|
minLength: 1,
|
|
706
797
|
examples: ['conversation:pair:agt_alice::agt_moza:world:dating-demo-world'],
|
|
707
798
|
}),
|
|
@@ -2,6 +2,7 @@ import { EventEmitter } from 'events';
|
|
|
2
2
|
import WebSocket from 'ws';
|
|
3
3
|
import { resolveClaworldRuntimeConfig } from './config-schema.js';
|
|
4
4
|
import { buildRuntimeAuthHeaders } from './account-identity.js';
|
|
5
|
+
import { buildClaworldRelayClientVersion } from '../plugin-version.js';
|
|
5
6
|
import { createRelayEventProtocol } from '../protocol/relay-event-protocol.js';
|
|
6
7
|
import { createInboundSessionRouter } from '../runtime/inbound-session-router.js';
|
|
7
8
|
import { createOutboundSessionBridge } from '../runtime/outbound-session-bridge.js';
|
|
@@ -486,7 +487,7 @@ export class ClaworldRelayClient extends EventEmitter {
|
|
|
486
487
|
config,
|
|
487
488
|
agentId,
|
|
488
489
|
credential = null,
|
|
489
|
-
clientVersion =
|
|
490
|
+
clientVersion = buildClaworldRelayClientVersion(),
|
|
490
491
|
sessionTarget,
|
|
491
492
|
fallbackTarget,
|
|
492
493
|
} = {}) {
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import repoPackageJson from '../../package.json' with { type: 'json' };
|
|
3
|
+
|
|
4
|
+
export const CLAWORLD_PLUGIN_PACKAGE_NAME = '@xfxstudio/claworld';
|
|
5
|
+
export const CLAWORLD_PLUGIN_VERSION_HEADER = 'x-claworld-plugin-version';
|
|
6
|
+
|
|
7
|
+
function normalizeText(value, fallback = null) {
|
|
8
|
+
if (value == null) return fallback;
|
|
9
|
+
const normalized = String(value).trim();
|
|
10
|
+
return normalized || fallback;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function normalizeHeaderValue(value) {
|
|
14
|
+
if (Array.isArray(value)) {
|
|
15
|
+
return normalizeHeaderValue(value[0]);
|
|
16
|
+
}
|
|
17
|
+
const normalized = normalizeText(value, null);
|
|
18
|
+
if (!normalized) return null;
|
|
19
|
+
return normalized.split(',')[0]?.trim() || null;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function normalizeClaworldPluginVersion(value, fallback = null) {
|
|
23
|
+
const normalized = normalizeText(value, null);
|
|
24
|
+
if (!normalized) return fallback;
|
|
25
|
+
const withoutPrefix = normalized.replace(/^v/i, '');
|
|
26
|
+
if (!/^\d+(?:\.\d+)*(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/.test(withoutPrefix)) {
|
|
27
|
+
return fallback;
|
|
28
|
+
}
|
|
29
|
+
return withoutPrefix;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function resolveCurrentPluginVersion() {
|
|
33
|
+
const repoVersion = normalizeClaworldPluginVersion(repoPackageJson?.version, null);
|
|
34
|
+
if (repoPackageJson?.name === CLAWORLD_PLUGIN_PACKAGE_NAME && repoVersion) {
|
|
35
|
+
return repoVersion;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
try {
|
|
39
|
+
const publishSurfaceSource = readFileSync(
|
|
40
|
+
new URL('../../packages/openclaw-plugin/package.json', import.meta.url),
|
|
41
|
+
'utf8',
|
|
42
|
+
);
|
|
43
|
+
const publishSurfacePackageJson = JSON.parse(publishSurfaceSource);
|
|
44
|
+
const publishSurfaceVersion = normalizeClaworldPluginVersion(publishSurfacePackageJson?.version, null);
|
|
45
|
+
if (publishSurfacePackageJson?.name === CLAWORLD_PLUGIN_PACKAGE_NAME && publishSurfaceVersion) {
|
|
46
|
+
return publishSurfaceVersion;
|
|
47
|
+
}
|
|
48
|
+
} catch {}
|
|
49
|
+
|
|
50
|
+
return repoVersion || '0.0.0';
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export const CLAWORLD_PLUGIN_CURRENT_VERSION = resolveCurrentPluginVersion();
|
|
54
|
+
|
|
55
|
+
export function readClaworldPluginVersionFromHeaders(headers = {}) {
|
|
56
|
+
const rawVersion = normalizeHeaderValue(headers?.[CLAWORLD_PLUGIN_VERSION_HEADER]);
|
|
57
|
+
return {
|
|
58
|
+
rawVersion,
|
|
59
|
+
reportedVersion: normalizeClaworldPluginVersion(rawVersion, rawVersion),
|
|
60
|
+
normalizedVersion: normalizeClaworldPluginVersion(rawVersion, null),
|
|
61
|
+
source: rawVersion ? CLAWORLD_PLUGIN_VERSION_HEADER : null,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function buildClaworldRelayClientVersion(version = CLAWORLD_PLUGIN_CURRENT_VERSION) {
|
|
66
|
+
return `claworld-plugin/${normalizeClaworldPluginVersion(version, CLAWORLD_PLUGIN_CURRENT_VERSION)}`;
|
|
67
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { resolveClaworldRuntimeConfig } from '../plugin/config-schema.js';
|
|
2
|
+
import { buildRuntimeAuthHeaders } from '../plugin/account-identity.js';
|
|
2
3
|
import { createRuntimeBoundaryError } from '../../lib/runtime-errors.js';
|
|
3
4
|
import { extractBackendErrorContext } from './backend-error-context.js';
|
|
4
5
|
import {
|
|
@@ -23,6 +24,11 @@ function normalizeStringList(values = []) {
|
|
|
23
24
|
return [...new Set(values.map((value) => normalizeText(value, null)).filter(Boolean))];
|
|
24
25
|
}
|
|
25
26
|
|
|
27
|
+
function normalizeWorldRole(worldRole, fallback = null) {
|
|
28
|
+
const normalized = normalizeText(worldRole, fallback);
|
|
29
|
+
return ['owner', 'member'].includes(normalized) ? normalized : fallback;
|
|
30
|
+
}
|
|
31
|
+
|
|
26
32
|
function sentenceCase(value, fallback = '') {
|
|
27
33
|
const normalized = normalizeText(value, fallback);
|
|
28
34
|
if (!normalized) return fallback;
|
|
@@ -129,6 +135,7 @@ function normalizeWorldDetail(payload = {}) {
|
|
|
129
135
|
displayName: normalizeText(payload.displayName, normalizedWorldId),
|
|
130
136
|
worldContextText: normalizeText(payload.worldContextText, ''),
|
|
131
137
|
ownerAgentId: normalizeText(payload.ownerAgentId, null),
|
|
138
|
+
worldRole: normalizeWorldRole(payload.worldRole, null),
|
|
132
139
|
enabled: typeof payload.enabled === 'boolean' ? payload.enabled : null,
|
|
133
140
|
requiredFieldCount: normalizeInteger(payload.requiredFieldCount, requiredFields.length) || requiredFields.length,
|
|
134
141
|
optionalFieldCount: normalizeInteger(payload.optionalFieldCount, optionalFields.length) || optionalFields.length,
|
|
@@ -168,6 +175,7 @@ function normalizeWorldDetail(payload = {}) {
|
|
|
168
175
|
displayName,
|
|
169
176
|
worldContextText: normalizeText(world.worldContextText || payload.worldContextText, ''),
|
|
170
177
|
ownerAgentId: normalizeText(management.ownerAgentId, null),
|
|
178
|
+
worldRole: normalizeWorldRole(payload.worldRole, null),
|
|
171
179
|
enabled: typeof management.enabled === 'boolean' ? management.enabled : null,
|
|
172
180
|
statusLabel: normalizeText(management.status, null),
|
|
173
181
|
requiredFieldCount: 1,
|
|
@@ -259,6 +267,7 @@ function normalizeCandidate(candidate = {}, index = 0) {
|
|
|
259
267
|
return {
|
|
260
268
|
candidateId: normalizeText(candidate.candidateId, `candidate_${index + 1}`),
|
|
261
269
|
worldId: normalizeText(candidate.worldId, 'unknown-world'),
|
|
270
|
+
worldRole: normalizeWorldRole(candidate.worldRole, null),
|
|
262
271
|
sourceMembershipId: normalizeText(candidate.sourceMembershipId, null),
|
|
263
272
|
online: candidate.online === true,
|
|
264
273
|
displayName,
|
|
@@ -314,7 +323,7 @@ function normalizeCandidateFeedResponse(payload = {}, { worldId = null, agentId
|
|
|
314
323
|
};
|
|
315
324
|
}
|
|
316
325
|
|
|
317
|
-
function normalizeWorldJoinResponse(payload = {}, { worldId = null, agentId = null } = {}) {
|
|
326
|
+
export function normalizeWorldJoinResponse(payload = {}, { worldId = null, agentId = null } = {}) {
|
|
318
327
|
const membership = payload.membership && typeof payload.membership === 'object' ? payload.membership : null;
|
|
319
328
|
const normalizedWorldId = normalizeText(payload.worldId, worldId || 'unknown-world');
|
|
320
329
|
const normalizedAgentId = normalizeText(payload.agentId || membership?.agentId, agentId || null);
|
|
@@ -330,6 +339,7 @@ function normalizeWorldJoinResponse(payload = {}, { worldId = null, agentId = nu
|
|
|
330
339
|
status: normalizeText(payload.status, membershipStatus === 'active' ? 'joined' : 'accepted'),
|
|
331
340
|
worldId: normalizedWorldId,
|
|
332
341
|
agentId: normalizedAgentId,
|
|
342
|
+
worldRole: normalizeWorldRole(payload.worldRole, null),
|
|
333
343
|
membershipStatus,
|
|
334
344
|
participantContextText: normalizeText(
|
|
335
345
|
payload.participantContextText,
|
|
@@ -512,11 +522,10 @@ export async function fetchWorldDetail({
|
|
|
512
522
|
const resolvedRuntimeConfig = runtimeConfig || resolveClaworldRuntimeConfig(cfg, accountId);
|
|
513
523
|
const baseUrl = normalizeRelayHttpBaseUrl(resolvedRuntimeConfig.serverUrl);
|
|
514
524
|
const detail = await fetchJson(fetchImpl, `${baseUrl}/v1/worlds/${encodeURIComponent(resolvedWorldId)}`, {
|
|
515
|
-
headers: {
|
|
525
|
+
headers: buildRuntimeAuthHeaders(resolvedRuntimeConfig, {
|
|
516
526
|
accept: 'application/json',
|
|
517
527
|
...(resolvedRuntimeConfig.apiKey ? { 'x-api-key': resolvedRuntimeConfig.apiKey } : {}),
|
|
518
|
-
|
|
519
|
-
},
|
|
528
|
+
}),
|
|
520
529
|
});
|
|
521
530
|
|
|
522
531
|
if (!detail.ok) {
|
|
@@ -563,12 +572,11 @@ export async function joinWorld({
|
|
|
563
572
|
const baseUrl = normalizeRelayHttpBaseUrl(resolvedRuntimeConfig.serverUrl);
|
|
564
573
|
const joinResult = await fetchJson(fetchImpl, `${baseUrl}/v1/worlds/${encodeURIComponent(resolvedWorldId)}/join`, {
|
|
565
574
|
method: 'POST',
|
|
566
|
-
headers: {
|
|
575
|
+
headers: buildRuntimeAuthHeaders(resolvedRuntimeConfig, {
|
|
567
576
|
accept: 'application/json',
|
|
568
577
|
'content-type': 'application/json',
|
|
569
578
|
...(resolvedRuntimeConfig.apiKey ? { 'x-api-key': resolvedRuntimeConfig.apiKey } : {}),
|
|
570
|
-
|
|
571
|
-
},
|
|
579
|
+
}),
|
|
572
580
|
body: JSON.stringify({
|
|
573
581
|
agentId: resolvedAgentId,
|
|
574
582
|
participantContextText: normalizeText(participantContextText, null),
|
|
@@ -628,11 +636,10 @@ export async function fetchWorldCandidateFeed({
|
|
|
628
636
|
requestUrl.searchParams.set('limit', String(normalizedLimit));
|
|
629
637
|
}
|
|
630
638
|
const candidateFeed = await fetchJson(fetchImpl, requestUrl.toString(), {
|
|
631
|
-
headers: {
|
|
639
|
+
headers: buildRuntimeAuthHeaders(resolvedRuntimeConfig, {
|
|
632
640
|
accept: 'application/json',
|
|
633
641
|
...(resolvedRuntimeConfig.apiKey ? { 'x-api-key': resolvedRuntimeConfig.apiKey } : {}),
|
|
634
|
-
|
|
635
|
-
},
|
|
642
|
+
}),
|
|
636
643
|
});
|
|
637
644
|
|
|
638
645
|
if (!candidateFeed.ok) {
|
|
@@ -674,11 +681,10 @@ export async function resolveWorldSelectionFlow({
|
|
|
674
681
|
const resolvedRuntimeConfig = runtimeConfig || resolveClaworldRuntimeConfig(cfg, accountId);
|
|
675
682
|
const baseUrl = normalizeRelayHttpBaseUrl(resolvedRuntimeConfig.serverUrl);
|
|
676
683
|
const worlds = await fetchJson(fetchImpl, `${baseUrl}/v1/worlds`, {
|
|
677
|
-
headers: {
|
|
684
|
+
headers: buildRuntimeAuthHeaders(resolvedRuntimeConfig, {
|
|
678
685
|
accept: 'application/json',
|
|
679
686
|
...(resolvedRuntimeConfig.apiKey ? { 'x-api-key': resolvedRuntimeConfig.apiKey } : {}),
|
|
680
|
-
|
|
681
|
-
},
|
|
687
|
+
}),
|
|
682
688
|
});
|
|
683
689
|
|
|
684
690
|
if (!worlds.ok) {
|
|
@@ -161,6 +161,7 @@ function projectToolCandidateDeliverySummary(
|
|
|
161
161
|
candidateId: normalizeText(summary.candidateId, null),
|
|
162
162
|
sourceMembershipId: normalizeText(summary.sourceMembershipId, null),
|
|
163
163
|
displayName: normalizeText(summary.displayName, null),
|
|
164
|
+
worldRole: projectWorldRole(summary.worldRole, null),
|
|
164
165
|
headline: normalizeText(summary.headline, null),
|
|
165
166
|
online: summary.online === true,
|
|
166
167
|
rank: normalizeOptionalInteger(summary.rank, null),
|
|
@@ -231,9 +232,13 @@ export function projectToolWorldDetail(worldDetail = {}) {
|
|
|
231
232
|
worldDetail.world?.worldContextText,
|
|
232
233
|
worldDetail.worldContextText || '',
|
|
233
234
|
),
|
|
234
|
-
ownerAgentId: normalizeText(
|
|
235
|
-
|
|
236
|
-
|
|
235
|
+
ownerAgentId: normalizeText(
|
|
236
|
+
worldDetail.management?.ownerAgentId,
|
|
237
|
+
normalizeText(worldDetail.ownerAgentId, null),
|
|
238
|
+
),
|
|
239
|
+
worldRole: projectWorldRole(worldDetail.worldRole, null),
|
|
240
|
+
enabled: normalizeOptionalBoolean(worldDetail.management?.enabled, normalizeOptionalBoolean(worldDetail.enabled, null)),
|
|
241
|
+
status: normalizeText(worldDetail.management?.status, normalizeText(worldDetail.statusLabel, null)),
|
|
237
242
|
participantContextField: projectParticipantContextField(worldDetail.participantContextField),
|
|
238
243
|
};
|
|
239
244
|
}
|
|
@@ -243,6 +248,7 @@ function projectToolCandidateSummary(summary = {}, index = 0) {
|
|
|
243
248
|
candidateId: normalizeText(summary.candidateId, `candidate_${index + 1}`),
|
|
244
249
|
displayName: normalizeText(summary.displayName, `Candidate ${index + 1}`),
|
|
245
250
|
agentCode: normalizeText(summary.agentCode, null)?.toUpperCase() || null,
|
|
251
|
+
worldRole: projectWorldRole(summary.worldRole, null),
|
|
246
252
|
headline: normalizeText(summary.headline, null),
|
|
247
253
|
online: summary.online === true,
|
|
248
254
|
rank: normalizeInteger(summary.rank, 0) || null,
|
|
@@ -304,6 +310,7 @@ export function projectToolJoinWorldResponse(
|
|
|
304
310
|
status: joinResult.membershipStatus === 'active' ? 'joined' : 'accepted',
|
|
305
311
|
worldId: joinResult.worldId,
|
|
306
312
|
accountId: normalizeText(accountId, null),
|
|
313
|
+
worldRole: projectWorldRole(joinResult.worldRole, null),
|
|
307
314
|
membershipStatus: joinResult.membershipStatus || 'unknown',
|
|
308
315
|
participantContextText: normalizeText(
|
|
309
316
|
joinResult.participantContextText,
|
|
@@ -329,6 +336,10 @@ export function projectToolCreateWorldResponse(world = {}, { accountId = null }
|
|
|
329
336
|
status: normalizeText(world.status, null),
|
|
330
337
|
enabled: normalizeOptionalBoolean(world.enabled, null),
|
|
331
338
|
worldRole: projectWorldRole(world.worldRole, null),
|
|
339
|
+
ownerJoin:
|
|
340
|
+
world.ownerJoin && typeof world.ownerJoin === 'object'
|
|
341
|
+
? projectToolJoinWorldResponse(world.ownerJoin, { accountId })
|
|
342
|
+
: null,
|
|
332
343
|
schemaVersion: normalizeOptionalInteger(world.schemaVersion, null),
|
|
333
344
|
createdAt: normalizeText(world.createdAt, null),
|
|
334
345
|
};
|
|
@@ -373,6 +384,41 @@ export function projectToolManagedWorldResponse(world = {}, { accountId = null }
|
|
|
373
384
|
};
|
|
374
385
|
}
|
|
375
386
|
|
|
387
|
+
function projectToolWorldMembershipSummary(membership = {}) {
|
|
388
|
+
return {
|
|
389
|
+
membershipId: normalizeText(membership.membershipId, null),
|
|
390
|
+
worldId: normalizeText(membership.worldId, null),
|
|
391
|
+
displayName: normalizeText(membership.displayName, null),
|
|
392
|
+
worldContextText: normalizeText(membership.worldContextText, null),
|
|
393
|
+
ownerAgentId: normalizeText(membership.ownerAgentId, null),
|
|
394
|
+
enabled: normalizeOptionalBoolean(membership.enabled, null),
|
|
395
|
+
worldStatus: normalizeText(membership.worldStatus, null),
|
|
396
|
+
worldRole: projectWorldRole(membership.worldRole, null),
|
|
397
|
+
membershipStatus: normalizeText(membership.membershipStatus, null),
|
|
398
|
+
participantContextText: normalizeText(membership.participantContextText, null),
|
|
399
|
+
joinedAt: normalizeText(membership.joinedAt, null),
|
|
400
|
+
updatedAt: normalizeText(membership.updatedAt, null),
|
|
401
|
+
nextAction: normalizeText(membership.nextAction, null),
|
|
402
|
+
};
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
export function projectToolWorldMembershipListResponse(payload = {}, { accountId = null } = {}) {
|
|
406
|
+
return {
|
|
407
|
+
accountId: normalizeText(accountId, null),
|
|
408
|
+
memberships: Array.isArray(payload.items)
|
|
409
|
+
? payload.items.map((membership) => projectToolWorldMembershipSummary(membership))
|
|
410
|
+
: [],
|
|
411
|
+
nextAction: normalizeText(payload.nextAction, null),
|
|
412
|
+
};
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
export function projectToolWorldMembershipResponse(payload = {}, { accountId = null } = {}) {
|
|
416
|
+
return {
|
|
417
|
+
accountId: normalizeText(accountId, null),
|
|
418
|
+
...projectToolWorldMembershipSummary(payload),
|
|
419
|
+
};
|
|
420
|
+
}
|
|
421
|
+
|
|
376
422
|
export function projectToolFeedbackSubmissionResponse(result = {}) {
|
|
377
423
|
const feedback = result.feedback && typeof result.feedback === 'object' ? result.feedback : {};
|
|
378
424
|
const reporter = feedback.reporter && typeof feedback.reporter === 'object' ? feedback.reporter : {};
|