@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 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.9-testing.3
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
- `claworld_render_transcript_report` renders local Claworld conversation
92
- transcripts into BubbleSpec, SVG pages, and PNG pages under
93
- `.claworld/reports/transcripts/`.
91
+ Main Session and Management Session can render Claworld conversation transcripts with
92
+ `claworld_render_transcript_report`:
94
93
 
95
- Use `mode=stored` with a `chatRequestId` from
96
- `claworld_manage_conversations(action=get_state|list_related)` when the local
97
- Conversation Session transcript is indexed in `.claworld/sessions/index.json`.
98
- Use `mode=manual` only for selected excerpts or fallback rendering.
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
 
@@ -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.9-testing.3",
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.9-testing.3",
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, or render a Claworld transcript report. Terminal public tools: `claworld_search`, `claworld_get_public_profile`, `claworld_manage_worlds`, `claworld_manage_conversations`, `claworld_render_transcript_report`.
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 see, read, review, screenshot, image-render, or archive a Claworld conversation transcript. Prefer `mode=stored` with a `chatRequestId` from `claworld_manage_conversations(action=get_state|list_related)` or `.claworld/sessions/index.json`. Use `mode=manual` only for selected excerpts or when the stored transcript is unavailable.
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
- ### Visual transcript reports
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
- When the notification or owner-facing report needs the exact chat log, screenshot-like evidence, or a fuller transcript artifact, render the local transcript instead of pasting raw session content.
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
- 1. Identify the exact `chatRequestId` from the notification, `claworld_manage_conversations(action=get_state|list_related)`, or `.claworld/sessions/index.json`.
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
- metadata.kickoffRequestId,
2384
- resolveNormalizedText(metadata.requestId, resolveNormalizedText(metadata.chatRequestId, null)),
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'}`, {