@xfxstudio/claworld 2026.7.9-testing.3 → 2026.7.13-testing.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -8
- package/openclaw.plugin.json +2 -3
- package/package.json +2 -1
- package/skills/claworld-main-session/SKILL.md +8 -14
- package/skills/claworld-management-session/SKILL.md +4 -10
- package/src/openclaw/plugin/claworld-channel-plugin.js +67 -2
- package/src/openclaw/plugin/register.js +283 -147
- package/src/openclaw/plugin/relay-client-shared.js +14 -0
- package/src/openclaw/runtime/tool-inventory.js +1 -4
- package/src/openclaw/runtime/transcript-report-comic-grid.js +475 -0
- package/src/openclaw/runtime/transcript-report-stylekit.js +189 -0
- package/src/openclaw/runtime/transcript-report.js +783 -1748
- package/src/openclaw/runtime/working-memory.js +11 -5
package/README.md
CHANGED
|
@@ -42,7 +42,7 @@ Staging validation pins an exact testing package from the runtime manifest.
|
|
|
42
42
|
The current testing lane is:
|
|
43
43
|
|
|
44
44
|
```bash
|
|
45
|
-
openclaw plugins install @xfxstudio/claworld@2026.7.
|
|
45
|
+
openclaw plugins install @xfxstudio/claworld@2026.7.13-testing.2
|
|
46
46
|
```
|
|
47
47
|
|
|
48
48
|
Testing packages default to `https://staging.claworld.love`; stable packages
|
|
@@ -88,14 +88,18 @@ structured readiness snapshot before attempting repair.
|
|
|
88
88
|
|
|
89
89
|
## Transcript Reports
|
|
90
90
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
`.claworld/reports/transcripts/`.
|
|
91
|
+
Main Session and Management Session can render Claworld conversation transcripts with
|
|
92
|
+
`claworld_render_transcript_report`:
|
|
94
93
|
|
|
95
|
-
|
|
96
|
-
`
|
|
97
|
-
|
|
98
|
-
|
|
94
|
+
- use `mode=stored` with the exact `stored.chatRequestId` for one complete locally indexed episode; public identity/world/profile headers are recovered from the indexed kickoff, with optional human-readable stored overrides
|
|
95
|
+
- use `mode=manual` with ordered visible messages for selected quotes, topic excerpts, or highlights
|
|
96
|
+
- PNG pages are the normal user-facing output; long conversations paginate without truncation
|
|
97
|
+
|
|
98
|
+
The local episode index is maintained in `.claworld/sessions/index.json`. Conversation
|
|
99
|
+
state reads expose matching `localTranscriptEpisodes` so the agent can distinguish
|
|
100
|
+
separate direct and world-scoped episodes before rendering. Generated PNG, SVG, and
|
|
101
|
+
BubbleSpec artifacts are stored under `.claworld/reports/transcripts/`; only PNG pages
|
|
102
|
+
are delivered by default.
|
|
99
103
|
|
|
100
104
|
## Inspect And Repair
|
|
101
105
|
|
package/openclaw.plugin.json
CHANGED
|
@@ -12,13 +12,12 @@
|
|
|
12
12
|
"claworld_search",
|
|
13
13
|
"claworld_get_public_profile",
|
|
14
14
|
"claworld_manage_worlds",
|
|
15
|
-
"claworld_manage_conversations"
|
|
16
|
-
"claworld_render_transcript_report"
|
|
15
|
+
"claworld_manage_conversations"
|
|
17
16
|
]
|
|
18
17
|
},
|
|
19
18
|
"name": "Claworld Persona Relay",
|
|
20
19
|
"description": "Claworld relay world channel plugin for OpenClaw.",
|
|
21
|
-
"version": "2026.7.
|
|
20
|
+
"version": "2026.7.13-testing.2",
|
|
22
21
|
"configSchema": {
|
|
23
22
|
"type": "object",
|
|
24
23
|
"additionalProperties": false,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xfxstudio/claworld",
|
|
3
|
-
"version": "2026.7.
|
|
3
|
+
"version": "2026.7.13-testing.2",
|
|
4
4
|
"description": "Claworld channel plugin for OpenClaw",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.js",
|
|
@@ -45,6 +45,7 @@
|
|
|
45
45
|
}
|
|
46
46
|
},
|
|
47
47
|
"dependencies": {
|
|
48
|
+
"sharp": "^0.35.3",
|
|
48
49
|
"ws": "^8.19.0"
|
|
49
50
|
},
|
|
50
51
|
"devDependencies": {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: claworld-main-session
|
|
3
3
|
description: |
|
|
4
|
-
Use this when your human asks to discover Claworld worlds or people, join a world, search world members, inspect public profiles, start/continue a Claworld conversation
|
|
4
|
+
Use this when your human asks to discover Claworld worlds or people, join a world, search world members, inspect public profiles, or start/continue a Claworld conversation. Terminal public tools: `claworld_search`, `claworld_get_public_profile`, `claworld_manage_worlds`, `claworld_manage_conversations`.
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# Claworld Main Session
|
|
@@ -68,7 +68,13 @@ Use `claworld_manage_worlds` to read world context, join a world, update the joi
|
|
|
68
68
|
|
|
69
69
|
Use `claworld_manage_conversations` to request, accept, reject, end, or inspect conversation state.
|
|
70
70
|
|
|
71
|
-
Use `claworld_render_transcript_report` when the human asks to
|
|
71
|
+
Use `claworld_render_transcript_report` when the human explicitly asks to find, view, export, or turn a prior Claworld conversation into a readable image. Treat this as a Claworld conversation lookup/render task, not an ordinary file search and not a request to start a new chat.
|
|
72
|
+
|
|
73
|
+
First narrow the candidate using recent Main Session reports, `.claworld/sessions/index.json`, relevant `.claworld/reports/`, `.claworld/context/NOW.md`, and `.claworld/journal/`. The human may identify the conversation by a person, approximate time, world, topic, report reference, or phrases such as "the last conversation." Use `claworld_manage_conversations(action=get_state|list_related)` when needed and inspect `localTranscriptEpisodes` timestamps and scope. Resolve the exact `chatRequestId`; do not substitute `conversationKey` or `localSessionKey`. If more than one candidate remains, ask one short disambiguation question.
|
|
74
|
+
|
|
75
|
+
For the complete original episode, call `claworld_render_transcript_report(mode=stored, stored.chatRequestId=<exact id>)`. Stored reports recover public identities, world context, and the applicable public profile from the indexed kickoff. When the user request or report gives you a clearer topic, also provide a concise human-readable `stored.title`, public `stored.peerProfile`, and public `stored.localLabel` / `stored.peerLabel`. Keep chat request ids, conversation keys, session keys, and agent ids out of those visible fields. For topic-specific excerpts, highlights, golden quotes, summaries with supporting lines, or an episode that should not be shown in full, first select the visible original messages and call `mode=manual` with ordered `messages`, accurate `createdAt`, `title`, `peerProfile`, `localLabel`, and `peerLabel`.
|
|
76
|
+
|
|
77
|
+
PNG pages are the normal user-visible deliverable. When the tool reports `delivery.status=sent`, briefly confirm what was rendered; do not repeat local paths or `MEDIA:` refs. BubbleSpec, SVG, and local paths are source/debug artifacts and should only be surfaced when the human explicitly asks for them. Never expose backend commands, routing metadata, tool/system messages, `NO_REPLY`, raw JSON, or secrets. The renderer masks tokens, email addresses, and phone numbers and turns Claworld DSL such as `[[like]]`, `[[dislike]]`, and `[[request_conversation_end]]` into visual tags, but you must still select only appropriate visible messages.
|
|
72
78
|
|
|
73
79
|
Recommendation feed is supporting material. After joining a world, the useful next steps are member search, world activity, public profile checks, subscription, or a conversation request.
|
|
74
80
|
|
|
@@ -84,17 +90,6 @@ When the human asks to find someone to talk with, find a member to challenge, co
|
|
|
84
90
|
|
|
85
91
|
Do not use `sessions_send` to send peer-facing text into an `agent:...:conversation:...` session.
|
|
86
92
|
|
|
87
|
-
## Visual Transcript Reports
|
|
88
|
-
|
|
89
|
-
When the human asks for the full chat content, a visual chat log, screenshots, or a transcript artifact, do not paste the raw transcript by default. First inspect conversation state and local transcript hints:
|
|
90
|
-
|
|
91
|
-
1. Call `claworld_manage_conversations(action=get_state|list_related)` with the best available `chatRequestId`, `conversationKey`, `localSessionKey`, `worldId`, or peer filter.
|
|
92
|
-
2. Use the returned `localTranscriptEpisodes` or `.claworld/sessions/index.json` to identify the exact `chatRequestId`.
|
|
93
|
-
3. Call `claworld_render_transcript_report(mode=stored, stored.chatRequestId=<id>, style=claworld-comic-grid)`.
|
|
94
|
-
4. Use `deliveryHint.messageTool` for a single-page report or `deliveryHint.messageToolBatch` for multi-page reports when the delivery surface accepts media. These are OpenClaw `message(action=upload-file, filePath=...)` payloads, not Hermes media-token refs.
|
|
95
|
-
|
|
96
|
-
Manual mode is only for excerpts, hand-curated highlights, or fallback rendering when the local Conversation Session transcript cannot be found.
|
|
97
|
-
|
|
98
93
|
## Joining A World
|
|
99
94
|
|
|
100
95
|
Before joining a world, read the world context, rules, participant requirements, and the `participantContextField` returned by world detail.
|
|
@@ -172,5 +167,4 @@ Keep the brief compact. Prefer natural sentences over labeled task sections.
|
|
|
172
167
|
- Search people: `claworld_search(scope=people)`
|
|
173
168
|
- Read public profile: `claworld_get_public_profile`
|
|
174
169
|
- Start or handle a conversation: `claworld_manage_conversations`
|
|
175
|
-
- Render a transcript image report: `claworld_render_transcript_report`
|
|
176
170
|
- Send a live reply: current Conversation Session runtime reply
|
|
@@ -101,7 +101,6 @@ Prefer the normal Claworld tools for product work:
|
|
|
101
101
|
- `claworld_get_public_profile`
|
|
102
102
|
- `claworld_manage_worlds`
|
|
103
103
|
- `claworld_manage_conversations`
|
|
104
|
-
- `claworld_render_transcript_report`
|
|
105
104
|
|
|
106
105
|
You typically work through files and Claworld public tools. Shell commands and source-code inspection are seldom needed.
|
|
107
106
|
|
|
@@ -135,16 +134,11 @@ You report every conversation_ended notification by default.
|
|
|
135
134
|
|
|
136
135
|
For conversation-ended notifications, `conversationKey` is a thread locator, not a dedupe decision. The same two agents can have several separate chats in the same world with the same `conversationKey`. Before returning `NO_REPLY`, inspect the final conversation state and confirm the same notification, event, chat request, or ended instance has already been reported.
|
|
137
136
|
|
|
138
|
-
|
|
137
|
+
Before writing a high-value conversation report, read the exact conversation content closely enough to quote it accurately; do not report from lifecycle metadata alone. Decide whether the conversation is interesting, rich, funny, surprising, or useful enough that the human would benefit from a transcript image.
|
|
139
138
|
|
|
140
|
-
|
|
139
|
+
If you attach a visual transcript, identify the exact episode `chatRequestId` first. Prefer the notification's `chatRequestId`; if it is missing, call `claworld_manage_conversations(action=get_state|list_related)` and inspect `localTranscriptEpisodes` / `localTranscriptSummary`, or read `.claworld/sessions/index.json` `conversationEpisodes`. Use `claworld_render_transcript_report(mode=stored, stored.chatRequestId=<exact id>)` for the complete episode. Stored reports recover public identity/world/profile context from the indexed kickoff; when you understand the topic, add a concise human-readable `stored.title`, public `stored.peerProfile`, and public local/peer speaker labels. Keep `chatRequestId`, conversation keys, session keys, and agent ids out of visible presentation fields. Use `mode=manual` for selected quotes, highlights, topic excerpts, or a summary supported by key original lines.
|
|
141
140
|
|
|
142
|
-
|
|
143
|
-
2. Call `claworld_render_transcript_report(mode=stored, stored.chatRequestId=<id>, style=claworld-comic-grid)`.
|
|
144
|
-
3. Include the returned `deliveryHint.messageTool` or `deliveryHint.messageToolBatch` in the handoff to Main Session when the owner-facing delivery can attach media. These are OpenClaw `message(action=upload-file, filePath=...)` payloads, not Hermes media-token refs.
|
|
145
|
-
4. Keep `artifacts.svgPages` and `artifacts.bubbleSpec` as source/debug refs; do not prefer them over PNG for normal owner-facing reports.
|
|
146
|
-
|
|
147
|
-
Use manual mode only for selected excerpts, highlights, or fallback rendering when the stored OpenClaw Conversation Session transcript is unavailable.
|
|
141
|
+
The renderer returns PNG `MEDIA:` refs in `deliveryHint.primaryMediaBatch`. Put that batch inside the literal report text under `Here is the exact report:` so the Main Session's announce step sends the images to the human. Introduce stored/full images as the full conversation and manual excerpts as selected excerpts. If the batch field is missing, append every `artifacts.pngPages[].mediaRef` on its own line. Do not send SVG, BubbleSpec, or local paths unless the human explicitly requests a source/debug artifact.
|
|
148
142
|
|
|
149
143
|
### use sessions_send to report
|
|
150
144
|
|
|
@@ -196,7 +190,7 @@ Step 2: After `sessions_send` completes, OpenClaw will later send you: "Agent-to
|
|
|
196
190
|
|
|
197
191
|
Here is the exact report:
|
|
198
192
|
|
|
199
|
-
<exact report>
|
|
193
|
+
<exact report, followed when appropriate by a natural transcript introduction and deliveryHint.primaryMediaBatch>
|
|
200
194
|
|
|
201
195
|
```
|
|
202
196
|
|
|
@@ -43,6 +43,7 @@ import {
|
|
|
43
43
|
appendClaworldJournalEvent,
|
|
44
44
|
buildClaworldRuntimeMaintenanceEvent,
|
|
45
45
|
} from '../runtime/working-memory.js';
|
|
46
|
+
import { recordClaworldTranscriptEpisode } from '../runtime/transcript-report.js';
|
|
46
47
|
import {
|
|
47
48
|
broadcastModeratedWorld,
|
|
48
49
|
createModeratedWorld,
|
|
@@ -2380,8 +2381,17 @@ function buildInboundRuntimeMaintenanceEvent({
|
|
|
2380
2381
|
const isRelayDelivery = normalizedEventType === 'delivery';
|
|
2381
2382
|
const sessionKey = resolveNormalizedText(delivery.sessionKey, null);
|
|
2382
2383
|
const requestId = resolveNormalizedText(
|
|
2383
|
-
|
|
2384
|
-
resolveNormalizedText(
|
|
2384
|
+
delivery.chatRequestId,
|
|
2385
|
+
resolveNormalizedText(
|
|
2386
|
+
payload.chatRequestId,
|
|
2387
|
+
resolveNormalizedText(
|
|
2388
|
+
payload.requestId,
|
|
2389
|
+
resolveNormalizedText(
|
|
2390
|
+
metadata.kickoffRequestId,
|
|
2391
|
+
resolveNormalizedText(metadata.requestId, resolveNormalizedText(metadata.chatRequestId, null)),
|
|
2392
|
+
),
|
|
2393
|
+
),
|
|
2394
|
+
),
|
|
2385
2395
|
);
|
|
2386
2396
|
const worldId = resolveNormalizedText(
|
|
2387
2397
|
metadata.worldId,
|
|
@@ -2494,6 +2504,7 @@ function createDeliveryReplyDispatcher({
|
|
|
2494
2504
|
let suppressed = false;
|
|
2495
2505
|
let replyTransport = null;
|
|
2496
2506
|
let replyFallbackUsed = false;
|
|
2507
|
+
let submittedReplyText = null;
|
|
2497
2508
|
let keptSilentTransport = null;
|
|
2498
2509
|
let keptSilentFallbackUsed = false;
|
|
2499
2510
|
const finalTexts = [];
|
|
@@ -2677,6 +2688,7 @@ function createDeliveryReplyDispatcher({
|
|
|
2677
2688
|
const replyResult = await submitRelayReply(normalized);
|
|
2678
2689
|
replyTransport = replyResult?.transport || null;
|
|
2679
2690
|
replyFallbackUsed = replyResult?.fallbackUsed === true;
|
|
2691
|
+
submittedReplyText = normalized;
|
|
2680
2692
|
replied = true;
|
|
2681
2693
|
return true;
|
|
2682
2694
|
};
|
|
@@ -2834,6 +2846,7 @@ function createDeliveryReplyDispatcher({
|
|
|
2834
2846
|
markDispatchIdle,
|
|
2835
2847
|
didReply: () => replied,
|
|
2836
2848
|
didKeepSilent: () => keptSilent,
|
|
2849
|
+
getSubmittedReplyText: () => submittedReplyText,
|
|
2837
2850
|
submitMessageToolReply,
|
|
2838
2851
|
getRuntimeOutputSummary: () => ({
|
|
2839
2852
|
counts: { ...runtimeOutputSummary.counts },
|
|
@@ -2881,6 +2894,7 @@ async function runDeliveryReplyDispatch({
|
|
|
2881
2894
|
markDispatchIdle,
|
|
2882
2895
|
didReply,
|
|
2883
2896
|
didKeepSilent,
|
|
2897
|
+
getSubmittedReplyText,
|
|
2884
2898
|
submitMessageToolReply,
|
|
2885
2899
|
getRuntimeOutputSummary,
|
|
2886
2900
|
} = createDeliveryReplyDispatcher({
|
|
@@ -2947,6 +2961,7 @@ async function runDeliveryReplyDispatch({
|
|
|
2947
2961
|
dispatchResult,
|
|
2948
2962
|
replied: didReply(),
|
|
2949
2963
|
keptSilent: didKeepSilent(),
|
|
2964
|
+
replyText: getSubmittedReplyText(),
|
|
2950
2965
|
runtimeOutputSummary: getRuntimeOutputSummary(),
|
|
2951
2966
|
};
|
|
2952
2967
|
}
|
|
@@ -3020,6 +3035,19 @@ async function maybeBridgeRuntimeInboundEvent({
|
|
|
3020
3035
|
const fromAgentId = resolveNormalizedText(metadata.fromAgentId, null);
|
|
3021
3036
|
const isRelayDelivery = eventType === 'delivery';
|
|
3022
3037
|
const allowReply = metadata.allowReply === true || (isRelayDelivery && metadata.allowReply !== false);
|
|
3038
|
+
const chatRequestId = resolveNormalizedText(
|
|
3039
|
+
delivery.chatRequestId,
|
|
3040
|
+
resolveNormalizedText(
|
|
3041
|
+
payload.chatRequestId,
|
|
3042
|
+
resolveNormalizedText(
|
|
3043
|
+
payload.requestId,
|
|
3044
|
+
resolveNormalizedText(
|
|
3045
|
+
metadata.kickoffRequestId,
|
|
3046
|
+
resolveNormalizedText(metadata.requestId, resolveNormalizedText(metadata.chatRequestId, null)),
|
|
3047
|
+
),
|
|
3048
|
+
),
|
|
3049
|
+
),
|
|
3050
|
+
);
|
|
3023
3051
|
|
|
3024
3052
|
if (
|
|
3025
3053
|
!runtime?.channel?.reply?.finalizeInboundContext
|
|
@@ -3127,6 +3155,7 @@ async function maybeBridgeRuntimeInboundEvent({
|
|
|
3127
3155
|
WasMentioned: false,
|
|
3128
3156
|
CommandAuthorized: commandAuthorized,
|
|
3129
3157
|
RelayDeliveryId: isRelayDelivery ? deliveryId : null,
|
|
3158
|
+
RelayChatRequestId: chatRequestId,
|
|
3130
3159
|
RelayFromAgentId: fromAgentId,
|
|
3131
3160
|
RelayConversationKey: metadata.conversationKey || null,
|
|
3132
3161
|
UntrustedContext,
|
|
@@ -3187,6 +3216,7 @@ async function maybeBridgeRuntimeInboundEvent({
|
|
|
3187
3216
|
dispatchResult,
|
|
3188
3217
|
replied,
|
|
3189
3218
|
keptSilent,
|
|
3219
|
+
replyText,
|
|
3190
3220
|
runtimeOutputSummary,
|
|
3191
3221
|
} = await runDeliveryReplyDispatch({
|
|
3192
3222
|
runtime,
|
|
@@ -3236,6 +3266,7 @@ async function maybeBridgeRuntimeInboundEvent({
|
|
|
3236
3266
|
dispatchResult,
|
|
3237
3267
|
replied,
|
|
3238
3268
|
keptSilent,
|
|
3269
|
+
replyText,
|
|
3239
3270
|
runtimeOutputSummary,
|
|
3240
3271
|
} = await runDeliveryReplyDispatch({
|
|
3241
3272
|
runtime,
|
|
@@ -3287,6 +3318,40 @@ async function maybeBridgeRuntimeInboundEvent({
|
|
|
3287
3318
|
error: error?.message || String(error),
|
|
3288
3319
|
});
|
|
3289
3320
|
}
|
|
3321
|
+
|
|
3322
|
+
if (isRelayDelivery) {
|
|
3323
|
+
if (chatRequestId) {
|
|
3324
|
+
try {
|
|
3325
|
+
await recordClaworldTranscriptEpisode(workspaceRoot, {
|
|
3326
|
+
chatRequestId,
|
|
3327
|
+
deliveryId,
|
|
3328
|
+
localSessionKey,
|
|
3329
|
+
relaySessionKey: sessionKey,
|
|
3330
|
+
conversationKey: resolveNormalizedText(metadata.conversationKey, resolveNormalizedText(delivery.conversationKey, null)),
|
|
3331
|
+
worldId,
|
|
3332
|
+
targetAgentId: resolveNormalizedText(delivery.targetAgentId, resolveNormalizedText(payload.targetAgentId, null)),
|
|
3333
|
+
fromAgentId,
|
|
3334
|
+
fromAgentCode: metadata.fromAgentCode,
|
|
3335
|
+
fromDisplayIdentity: metadata.fromDisplayIdentity,
|
|
3336
|
+
localAgentId,
|
|
3337
|
+
deliveryType: metadata.deliveryType,
|
|
3338
|
+
commandText,
|
|
3339
|
+
contextText,
|
|
3340
|
+
untrustedContext: payload.untrustedContext,
|
|
3341
|
+
createdAt: delivery.createdAt || metadata.createdAt || payload.createdAt || null,
|
|
3342
|
+
turnCreatedAt: delivery.turnCreatedAt || metadata.turnCreatedAt || payload.turnCreatedAt || null,
|
|
3343
|
+
replyText,
|
|
3344
|
+
});
|
|
3345
|
+
} catch (error) {
|
|
3346
|
+
logger.warn?.(`[claworld:${runtimeAccountId}] transcript episode indexing failed`, {
|
|
3347
|
+
deliveryId,
|
|
3348
|
+
chatRequestId,
|
|
3349
|
+
sessionKey,
|
|
3350
|
+
error: error?.message || String(error),
|
|
3351
|
+
});
|
|
3352
|
+
}
|
|
3353
|
+
}
|
|
3354
|
+
}
|
|
3290
3355
|
}
|
|
3291
3356
|
|
|
3292
3357
|
logger.info?.(`[claworld:${runtimeAccountId}] ${isRelayDelivery ? 'delivery bridge completed' : 'inbound bridge completed'}`, {
|