@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
|
@@ -27,9 +27,7 @@ import {
|
|
|
27
27
|
import { resolveOpenClawWorkspaceRoot } from '../runtime/workspace-resolver.js';
|
|
28
28
|
import {
|
|
29
29
|
augmentConversationPayloadWithLocalTranscriptIndex,
|
|
30
|
-
|
|
31
|
-
CLAWORLD_TRANSCRIPT_REPORT_TOOL_NAME,
|
|
32
|
-
renderClaworldTranscriptReport,
|
|
30
|
+
renderTranscriptReport,
|
|
33
31
|
} from '../runtime/transcript-report.js';
|
|
34
32
|
import { setClaworldRuntime } from './runtime.js';
|
|
35
33
|
import { PUBLIC_TOOL_ACTION_CATALOG } from '../../product-shell/contracts/search-item.js';
|
|
@@ -63,6 +61,159 @@ import {
|
|
|
63
61
|
withToolErrorBoundary,
|
|
64
62
|
} from './register-tooling.js';
|
|
65
63
|
|
|
64
|
+
function resolveShareCardToolResult(result) {
|
|
65
|
+
if (!Array.isArray(result?.content)) return null;
|
|
66
|
+
for (let index = 0; index < result.content.length; index += 1) {
|
|
67
|
+
const entry = result.content[index];
|
|
68
|
+
if (entry?.type !== 'text' || typeof entry.text !== 'string') continue;
|
|
69
|
+
try {
|
|
70
|
+
const payload = JSON.parse(entry.text);
|
|
71
|
+
const shareCard = normalizeObject(payload?.shareCard, null);
|
|
72
|
+
if (normalizeText(shareCard?.status, null) !== 'ready') continue;
|
|
73
|
+
const mediaUrl = normalizeText(
|
|
74
|
+
shareCard?.imageUrl,
|
|
75
|
+
normalizeText(shareCard?.downloadUrl, null),
|
|
76
|
+
);
|
|
77
|
+
if (!mediaUrl) continue;
|
|
78
|
+
return { index, entry, payload, shareCard, mediaUrl };
|
|
79
|
+
} catch {
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
async function deliverShareCardToCurrentChannel(api, result, toolContext = {}) {
|
|
87
|
+
const resolved = resolveShareCardToolResult(result);
|
|
88
|
+
if (!resolved) return result;
|
|
89
|
+
|
|
90
|
+
const route = toolContext?.deliveryContext || {};
|
|
91
|
+
const channel = normalizeText(route.channel, normalizeText(toolContext?.messageChannel, null));
|
|
92
|
+
const to = normalizeText(route.to, null);
|
|
93
|
+
if (!channel || !to) {
|
|
94
|
+
throw new Error('share-card delivery requires an active channel route');
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const loadAdapter = api?.runtime?.channel?.outbound?.loadAdapter;
|
|
98
|
+
if (typeof loadAdapter !== 'function') {
|
|
99
|
+
throw new Error('share-card delivery requires the OpenClaw channel outbound runtime');
|
|
100
|
+
}
|
|
101
|
+
const adapter = await loadAdapter(channel);
|
|
102
|
+
if (typeof adapter?.sendMedia !== 'function') {
|
|
103
|
+
throw new Error(`share-card delivery is unavailable for channel ${channel}`);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const deliveryResult = await adapter.sendMedia({
|
|
107
|
+
cfg: toolContext?.getRuntimeConfig?.() || toolContext?.runtimeConfig || api.config,
|
|
108
|
+
to,
|
|
109
|
+
text: '',
|
|
110
|
+
mediaUrl: resolved.mediaUrl,
|
|
111
|
+
...(normalizeText(route.accountId, null) ? { accountId: normalizeText(route.accountId, null) } : {}),
|
|
112
|
+
...(route.threadId != null ? { threadId: route.threadId } : {}),
|
|
113
|
+
});
|
|
114
|
+
if (deliveryResult?.success === false) {
|
|
115
|
+
throw new Error(`share-card delivery failed on channel ${channel}`);
|
|
116
|
+
}
|
|
117
|
+
const deliveryKind = normalizeText(deliveryResult?.receipt?.kind, null)?.toLowerCase();
|
|
118
|
+
if (deliveryKind && !['image', 'media'].includes(deliveryKind)) {
|
|
119
|
+
throw new Error(`share-card delivery did not produce native media on channel ${channel}`);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const content = [...result.content];
|
|
123
|
+
content[resolved.index] = {
|
|
124
|
+
...resolved.entry,
|
|
125
|
+
text: JSON.stringify({
|
|
126
|
+
...resolved.payload,
|
|
127
|
+
shareCard: {
|
|
128
|
+
...resolved.shareCard,
|
|
129
|
+
description: '分享卡图片已通过当前聊天渠道发送给用户。请只用一句普通文本确认分享卡已生成;不要下载、再次发送或输出 MEDIA:。',
|
|
130
|
+
},
|
|
131
|
+
}, null, 2),
|
|
132
|
+
};
|
|
133
|
+
return { ...result, content };
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
async function deliverTranscriptPagesToCurrentChannel(api, report, toolContext = {}) {
|
|
137
|
+
const pngPages = Array.isArray(report?.artifacts?.pngPages)
|
|
138
|
+
? report.artifacts.pngPages.filter((page) => normalizeText(page?.path, null))
|
|
139
|
+
: [];
|
|
140
|
+
if (pngPages.length === 0) return report;
|
|
141
|
+
|
|
142
|
+
const route = toolContext?.deliveryContext || {};
|
|
143
|
+
const channel = normalizeText(route.channel, normalizeText(toolContext?.messageChannel, null));
|
|
144
|
+
const to = normalizeText(route.to, null);
|
|
145
|
+
if (!channel || !to || channel === 'claworld') {
|
|
146
|
+
return {
|
|
147
|
+
...report,
|
|
148
|
+
delivery: {
|
|
149
|
+
status: 'media_refs_ready',
|
|
150
|
+
pageCount: pngPages.length,
|
|
151
|
+
description: 'PNG pages are ready. In Management Session, copy deliveryHint.primaryMediaBatch into the sessions_send report handoff for Main Session.',
|
|
152
|
+
},
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const loadAdapter = api?.runtime?.channel?.outbound?.loadAdapter;
|
|
157
|
+
if (typeof loadAdapter !== 'function') {
|
|
158
|
+
throw new Error('transcript delivery requires the OpenClaw channel outbound runtime');
|
|
159
|
+
}
|
|
160
|
+
const adapter = await loadAdapter(channel);
|
|
161
|
+
if (typeof adapter?.sendMedia !== 'function') {
|
|
162
|
+
throw new Error(`transcript delivery is unavailable for channel ${channel}`);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const receipts = [];
|
|
166
|
+
for (const page of pngPages) {
|
|
167
|
+
const deliveryResult = await adapter.sendMedia({
|
|
168
|
+
cfg: toolContext?.getRuntimeConfig?.() || toolContext?.runtimeConfig || api.config,
|
|
169
|
+
to,
|
|
170
|
+
text: '',
|
|
171
|
+
mediaUrl: page.path,
|
|
172
|
+
...(normalizeText(route.accountId, null) ? { accountId: normalizeText(route.accountId, null) } : {}),
|
|
173
|
+
...(route.threadId != null ? { threadId: route.threadId } : {}),
|
|
174
|
+
});
|
|
175
|
+
if (deliveryResult?.success === false) {
|
|
176
|
+
throw new Error(`transcript page ${page.page} delivery failed on channel ${channel}`);
|
|
177
|
+
}
|
|
178
|
+
const deliveryKind = normalizeText(deliveryResult?.receipt?.kind, null)?.toLowerCase();
|
|
179
|
+
if (deliveryKind && !['image', 'media'].includes(deliveryKind)) {
|
|
180
|
+
throw new Error(`transcript page ${page.page} delivery did not produce native media on channel ${channel}`);
|
|
181
|
+
}
|
|
182
|
+
receipts.push({
|
|
183
|
+
page: page.page,
|
|
184
|
+
messageId: normalizeText(deliveryResult?.messageId, null),
|
|
185
|
+
kind: deliveryKind || 'media',
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
return {
|
|
189
|
+
...report,
|
|
190
|
+
delivery: {
|
|
191
|
+
status: 'sent',
|
|
192
|
+
channel,
|
|
193
|
+
pageCount: receipts.length,
|
|
194
|
+
receipts,
|
|
195
|
+
description: 'All PNG transcript pages were sent through the current user-facing channel. Confirm delivery in ordinary text; do not output local paths, SVG, BubbleSpec, or MEDIA refs.',
|
|
196
|
+
},
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
async function resolveTranscriptWorkspace(api, params = {}, toolContext = {}) {
|
|
201
|
+
const cfg = await loadCurrentConfig(api);
|
|
202
|
+
const agentId = normalizeText(
|
|
203
|
+
toolContext?.agentId,
|
|
204
|
+
normalizeText(toolContext?.context?.agentId, normalizeText(params.agentId, null)),
|
|
205
|
+
);
|
|
206
|
+
return {
|
|
207
|
+
cfg,
|
|
208
|
+
agentId,
|
|
209
|
+
workspaceRoot: resolveOpenClawWorkspaceRoot({
|
|
210
|
+
sources: [toolContext, toolContext?.context, params],
|
|
211
|
+
config: cfg,
|
|
212
|
+
agentId,
|
|
213
|
+
}),
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
|
|
66
217
|
function buildClaworldStatusRoute(plugin) {
|
|
67
218
|
return {
|
|
68
219
|
method: 'GET',
|
|
@@ -481,118 +632,6 @@ function buildChatInboxFiltersParam({ description, worldIdProperty } = {}) {
|
|
|
481
632
|
});
|
|
482
633
|
}
|
|
483
634
|
|
|
484
|
-
function buildTranscriptReportParameters() {
|
|
485
|
-
return objectParam({
|
|
486
|
-
description: 'Render a Claworld transcript into BubbleSpec, SVG pages, and PNG pages.',
|
|
487
|
-
required: ['mode'],
|
|
488
|
-
additionalProperties: false,
|
|
489
|
-
properties: {
|
|
490
|
-
mode: stringParam({
|
|
491
|
-
description: 'Render source mode. Use stored for local OpenClaw conversation transcripts and manual for supplied excerpts.',
|
|
492
|
-
enumValues: ['stored', 'manual'],
|
|
493
|
-
examples: ['stored'],
|
|
494
|
-
}),
|
|
495
|
-
stored: objectParam({
|
|
496
|
-
description: 'Local OpenClaw transcript source resolved through .claworld/sessions/index.json.',
|
|
497
|
-
required: ['chatRequestId'],
|
|
498
|
-
additionalProperties: false,
|
|
499
|
-
properties: {
|
|
500
|
-
chatRequestId: stringParam({
|
|
501
|
-
description: 'Canonical Claworld chat request id to render from the local Conversation Session transcript index.',
|
|
502
|
-
minLength: 1,
|
|
503
|
-
examples: ['req_demo_1'],
|
|
504
|
-
}),
|
|
505
|
-
},
|
|
506
|
-
}),
|
|
507
|
-
manual: objectParam({
|
|
508
|
-
description: 'Explicit transcript excerpt fallback. Use only when the exact conversation is not available in local storage.',
|
|
509
|
-
required: ['messages', 'title', 'peerProfile', 'localLabel', 'peerLabel'],
|
|
510
|
-
additionalProperties: false,
|
|
511
|
-
properties: {
|
|
512
|
-
messages: arrayParam({
|
|
513
|
-
description: 'Ordered peer/local transcript messages.',
|
|
514
|
-
items: objectParam({
|
|
515
|
-
required: ['from', 'text', 'createdAt'],
|
|
516
|
-
additionalProperties: false,
|
|
517
|
-
properties: {
|
|
518
|
-
from: stringParam({
|
|
519
|
-
description: 'Speaker side from the owner account perspective.',
|
|
520
|
-
enumValues: ['peer', 'local'],
|
|
521
|
-
examples: ['peer'],
|
|
522
|
-
}),
|
|
523
|
-
text: stringParam({ description: 'Message text to render.', minLength: 1 }),
|
|
524
|
-
createdAt: stringParam({ description: 'Parseable timestamp for this message.', minLength: 1 }),
|
|
525
|
-
},
|
|
526
|
-
}),
|
|
527
|
-
}),
|
|
528
|
-
title: stringParam({ description: 'Report title.', minLength: 1 }),
|
|
529
|
-
peerProfile: stringParam({ description: 'Peer/profile subtitle shown under the report title.', minLength: 1 }),
|
|
530
|
-
localLabel: stringParam({ description: 'Label for the local side.', minLength: 1 }),
|
|
531
|
-
peerLabel: stringParam({ description: 'Label for the peer side.', minLength: 1 }),
|
|
532
|
-
},
|
|
533
|
-
}),
|
|
534
|
-
style: stringParam({
|
|
535
|
-
description: 'Transcript rendering style.',
|
|
536
|
-
enumValues: [CLAWORLD_TRANSCRIPT_REPORT_STYLE],
|
|
537
|
-
examples: [CLAWORLD_TRANSCRIPT_REPORT_STYLE],
|
|
538
|
-
}),
|
|
539
|
-
maxPageHeight: integerParam({
|
|
540
|
-
description: 'Maximum SVG/PNG page height before pagination.',
|
|
541
|
-
minimum: 900,
|
|
542
|
-
maximum: 8000,
|
|
543
|
-
examples: [2600],
|
|
544
|
-
}),
|
|
545
|
-
},
|
|
546
|
-
});
|
|
547
|
-
}
|
|
548
|
-
|
|
549
|
-
async function resolveLocalWorkspaceRootForTool(api, _plugin, params = {}) {
|
|
550
|
-
const cfg = await loadCurrentConfig(api);
|
|
551
|
-
const agentId = resolveToolAgentId(params, null);
|
|
552
|
-
return {
|
|
553
|
-
agentId,
|
|
554
|
-
workspaceRoot: resolveOpenClawWorkspaceRoot({
|
|
555
|
-
sources: [params],
|
|
556
|
-
config: cfg,
|
|
557
|
-
agentId,
|
|
558
|
-
}) || process.cwd(),
|
|
559
|
-
};
|
|
560
|
-
}
|
|
561
|
-
|
|
562
|
-
async function augmentConversationToolResultWithTranscripts(api, plugin, params, filters, result, toolName, action) {
|
|
563
|
-
const rewritten = rewriteToolResultName(result, toolName, action);
|
|
564
|
-
const payload = parseToolResultPayload(rewritten);
|
|
565
|
-
if (!payload || typeof payload !== 'object' || Array.isArray(payload)) return rewritten;
|
|
566
|
-
const { workspaceRoot } = await resolveLocalWorkspaceRootForTool(api, plugin, params);
|
|
567
|
-
const augmentedPayload = await augmentConversationPayloadWithLocalTranscriptIndex(payload, {
|
|
568
|
-
workspaceRoot,
|
|
569
|
-
filters,
|
|
570
|
-
});
|
|
571
|
-
return buildToolResult({
|
|
572
|
-
...augmentedPayload,
|
|
573
|
-
tool: toolName,
|
|
574
|
-
action,
|
|
575
|
-
});
|
|
576
|
-
}
|
|
577
|
-
|
|
578
|
-
function stripTranscriptReportToolContextParams(params = {}) {
|
|
579
|
-
const contextFields = new Set([
|
|
580
|
-
'workspaceRoot',
|
|
581
|
-
'workspaceDir',
|
|
582
|
-
'workspacePath',
|
|
583
|
-
'workspace',
|
|
584
|
-
'cwd',
|
|
585
|
-
'agent',
|
|
586
|
-
'context',
|
|
587
|
-
'session',
|
|
588
|
-
'agentId',
|
|
589
|
-
'localAgentId',
|
|
590
|
-
]);
|
|
591
|
-
return Object.fromEntries(
|
|
592
|
-
Object.entries(params || {}).filter(([key]) => !contextFields.has(key)),
|
|
593
|
-
);
|
|
594
|
-
}
|
|
595
|
-
|
|
596
635
|
function validateChatInboxFilterInput(filters = {}, action) {
|
|
597
636
|
const source = normalizeObject(filters, {}) || {};
|
|
598
637
|
for (const key of Object.keys(source)) {
|
|
@@ -698,7 +737,7 @@ function createTerminalToolAdapters(api, plugin, internalTools) {
|
|
|
698
737
|
const searchTool = 'claworld_search';
|
|
699
738
|
const manageWorldsTool = 'claworld_manage_worlds';
|
|
700
739
|
const manageConversationsTool = 'claworld_manage_conversations';
|
|
701
|
-
const
|
|
740
|
+
const renderTranscriptTool = 'claworld_render_transcript_report';
|
|
702
741
|
const accountTool = 'claworld_manage_account';
|
|
703
742
|
const publicProfileTool = 'claworld_get_public_profile';
|
|
704
743
|
|
|
@@ -1456,7 +1495,7 @@ function createTerminalToolAdapters(api, plugin, internalTools) {
|
|
|
1456
1495
|
}),
|
|
1457
1496
|
},
|
|
1458
1497
|
}),
|
|
1459
|
-
async execute(toolCallId, params = {}) {
|
|
1498
|
+
async execute(toolCallId, params = {}, toolContext = {}) {
|
|
1460
1499
|
const action = normalizeTerminalConversationAction(params.action, 'list_related', { throwOnInvalid: true });
|
|
1461
1500
|
if (action === 'request') {
|
|
1462
1501
|
const context = await resolveToolContext(api, plugin, params, {
|
|
@@ -1486,15 +1525,16 @@ function createTerminalToolAdapters(api, plugin, internalTools) {
|
|
|
1486
1525
|
action: 'list',
|
|
1487
1526
|
...(Object.keys(filters).length > 0 ? { filters } : {}),
|
|
1488
1527
|
});
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1528
|
+
const rewritten = rewriteToolResultName(result, manageConversationsTool, action);
|
|
1529
|
+
const payload = parseToolResultPayload(rewritten);
|
|
1530
|
+
if (!payload) return rewritten;
|
|
1531
|
+
const { workspaceRoot } = await resolveTranscriptWorkspace(api, params, toolContext);
|
|
1532
|
+
const augmented = await augmentConversationPayloadWithLocalTranscriptIndex({
|
|
1533
|
+
workspaceRoot,
|
|
1534
|
+
payload,
|
|
1493
1535
|
filters,
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
action,
|
|
1497
|
-
);
|
|
1536
|
+
});
|
|
1537
|
+
return buildToolResult(augmented);
|
|
1498
1538
|
}
|
|
1499
1539
|
if (action === 'accept' || action === 'reject') {
|
|
1500
1540
|
const result = await requireTerminalTool(internalTools, 'claworld_chat_inbox').execute(toolCallId, {
|
|
@@ -1526,30 +1566,97 @@ function createTerminalToolAdapters(api, plugin, internalTools) {
|
|
|
1526
1566
|
},
|
|
1527
1567
|
},
|
|
1528
1568
|
{
|
|
1529
|
-
name:
|
|
1569
|
+
name: renderTranscriptTool,
|
|
1530
1570
|
label: 'Claworld Render Transcript Report',
|
|
1531
|
-
description: 'Render
|
|
1571
|
+
description: 'Render one exact Claworld conversation episode or an agent-selected excerpt as the canonical Claworld comic-grid PNG transcript. Use mode=stored with stored.chatRequestId for a complete indexed episode. Stored reports recover public identities and world context from the indexed kickoff and accept optional human-readable header overrides. Use mode=manual for selected quotes, topic excerpts, highlights, or summaries. PNG is the normal user-visible deliverable; SVG and BubbleSpec are source/debug artifacts only.',
|
|
1532
1572
|
metadata: buildToolMetadata({
|
|
1533
|
-
category: '
|
|
1573
|
+
category: 'conversation',
|
|
1534
1574
|
usageNotes: [
|
|
1535
|
-
'Use
|
|
1536
|
-
'
|
|
1537
|
-
'
|
|
1538
|
-
'
|
|
1575
|
+
'Use stored.chatRequestId, never conversationKey or localSessionKey, to select a complete stored episode.',
|
|
1576
|
+
'Keep request, conversation, session, and agent ids out of stored title/profile/speaker-label overrides.',
|
|
1577
|
+
'For manual mode, provide only ordered visible peer/local messages and accurate createdAt timestamps.',
|
|
1578
|
+
'Main Session calls send all PNG pages through the current user-facing channel when available.',
|
|
1579
|
+
'Management Session calls return deliveryHint.primaryMediaBatch for inclusion in the sessions_send report handoff.',
|
|
1539
1580
|
],
|
|
1540
1581
|
}),
|
|
1541
|
-
parameters:
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1582
|
+
parameters: objectParam({
|
|
1583
|
+
description: 'Claworld transcript report render request.',
|
|
1584
|
+
required: ['mode'],
|
|
1585
|
+
properties: {
|
|
1586
|
+
mode: stringParam({
|
|
1587
|
+
description: 'stored renders one indexed local episode by chatRequestId; manual renders exactly manual.messages.',
|
|
1588
|
+
enumValues: ['stored', 'manual'],
|
|
1589
|
+
}),
|
|
1590
|
+
stored: objectParam({
|
|
1591
|
+
description: 'Stored episode selector. Provide only with mode=stored.',
|
|
1592
|
+
required: ['chatRequestId'],
|
|
1593
|
+
properties: {
|
|
1594
|
+
chatRequestId: stringParam({
|
|
1595
|
+
description: 'Canonical Claworld chat request / episode id.',
|
|
1596
|
+
minLength: 1,
|
|
1597
|
+
}),
|
|
1598
|
+
title: stringParam({
|
|
1599
|
+
description: 'Optional human-readable report title. Defaults to public peer/world context from the indexed kickoff.',
|
|
1600
|
+
minLength: 1,
|
|
1601
|
+
}),
|
|
1602
|
+
peerProfile: stringParam({
|
|
1603
|
+
description: 'Optional public subtitle/profile. Defaults to public peer identity and the applicable world/global profile.',
|
|
1604
|
+
minLength: 1,
|
|
1605
|
+
}),
|
|
1606
|
+
localLabel: stringParam({
|
|
1607
|
+
description: 'Optional public speaker label for local/right messages.',
|
|
1608
|
+
minLength: 1,
|
|
1609
|
+
}),
|
|
1610
|
+
peerLabel: stringParam({
|
|
1611
|
+
description: 'Optional public speaker label for peer/left messages.',
|
|
1612
|
+
minLength: 1,
|
|
1613
|
+
}),
|
|
1614
|
+
},
|
|
1615
|
+
}),
|
|
1616
|
+
manual: objectParam({
|
|
1617
|
+
description: 'Exact visible transcript rows and header context. Provide only with mode=manual.',
|
|
1618
|
+
required: ['messages', 'title', 'peerProfile', 'localLabel', 'peerLabel'],
|
|
1619
|
+
properties: {
|
|
1620
|
+
messages: arrayParam({
|
|
1621
|
+
description: 'Ordered visible transcript rows.',
|
|
1622
|
+
items: objectParam({
|
|
1623
|
+
required: ['from', 'text', 'createdAt'],
|
|
1624
|
+
properties: {
|
|
1625
|
+
from: stringParam({ description: 'peer=left; local=right.', enumValues: ['peer', 'local'] }),
|
|
1626
|
+
text: stringParam({ description: 'Visible message text.', minLength: 1 }),
|
|
1627
|
+
createdAt: stringParam({ description: 'Message timestamp, preferably ISO 8601.', minLength: 1 }),
|
|
1628
|
+
},
|
|
1629
|
+
}),
|
|
1630
|
+
}),
|
|
1631
|
+
title: stringParam({ description: 'Report header title.', minLength: 1 }),
|
|
1632
|
+
peerProfile: stringParam({ description: 'Peer profile/header subtitle appropriate to direct or world scope.', minLength: 1 }),
|
|
1633
|
+
localLabel: stringParam({ description: 'Speaker label for local/right messages.', minLength: 1 }),
|
|
1634
|
+
peerLabel: stringParam({ description: 'Speaker label for peer/left messages.', minLength: 1 }),
|
|
1635
|
+
},
|
|
1636
|
+
}),
|
|
1637
|
+
style: stringParam({
|
|
1638
|
+
description: 'Optional visual style. Defaults to the Hermes-compatible Claworld comic grid.',
|
|
1639
|
+
enumValues: ['claworld-comic-grid'],
|
|
1640
|
+
}),
|
|
1641
|
+
maxPageHeight: integerParam({
|
|
1642
|
+
description: 'Maximum page height in pixels. Long transcripts paginate without truncation.',
|
|
1643
|
+
minimum: 900,
|
|
1644
|
+
maximum: 8000,
|
|
1645
|
+
}),
|
|
1646
|
+
},
|
|
1647
|
+
}),
|
|
1648
|
+
async execute(_toolCallId, params = {}, toolContext = {}) {
|
|
1649
|
+
const { workspaceRoot, agentId } = await resolveTranscriptWorkspace(api, params, toolContext);
|
|
1650
|
+
if (!workspaceRoot) {
|
|
1651
|
+
throw new Error('unable to resolve the active OpenClaw workspace for transcript rendering');
|
|
1652
|
+
}
|
|
1653
|
+
const report = await renderTranscriptReport({
|
|
1545
1654
|
workspaceRoot,
|
|
1546
|
-
agentId,
|
|
1547
|
-
args:
|
|
1548
|
-
});
|
|
1549
|
-
return buildToolResult({
|
|
1550
|
-
...payload,
|
|
1551
|
-
tool: transcriptReportTool,
|
|
1655
|
+
localAgentId: agentId,
|
|
1656
|
+
args: params,
|
|
1552
1657
|
});
|
|
1658
|
+
const delivered = await deliverTranscriptPagesToCurrentChannel(api, report, toolContext);
|
|
1659
|
+
return buildToolResult({ ...delivered, tool: renderTranscriptTool });
|
|
1553
1660
|
},
|
|
1554
1661
|
},
|
|
1555
1662
|
];
|
|
@@ -2595,11 +2702,40 @@ export function registerClaworldPluginFull(api, plugin) {
|
|
|
2595
2702
|
const internalTools = new Map(
|
|
2596
2703
|
buildRegisteredTools(api, plugin).map((tool) => [tool.name, tool]),
|
|
2597
2704
|
);
|
|
2598
|
-
for (const
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
|
|
2705
|
+
for (const terminalTool of createTerminalToolAdapters(api, plugin, internalTools)) {
|
|
2706
|
+
if (terminalTool.name === 'claworld_manage_account') {
|
|
2707
|
+
api.registerTool(
|
|
2708
|
+
(toolContext) => ({
|
|
2709
|
+
...terminalTool,
|
|
2710
|
+
execute: withToolErrorBoundary(terminalTool.name, async (...args) => {
|
|
2711
|
+
const result = await terminalTool.execute(...args);
|
|
2712
|
+
return await deliverShareCardToCurrentChannel(api, result, toolContext);
|
|
2713
|
+
}),
|
|
2714
|
+
}),
|
|
2715
|
+
{ name: terminalTool.name },
|
|
2716
|
+
);
|
|
2717
|
+
continue;
|
|
2718
|
+
}
|
|
2719
|
+
if (
|
|
2720
|
+
terminalTool.name === 'claworld_manage_conversations'
|
|
2721
|
+
|| terminalTool.name === 'claworld_render_transcript_report'
|
|
2722
|
+
) {
|
|
2723
|
+
api.registerTool(
|
|
2724
|
+
(toolContext) => ({
|
|
2725
|
+
...terminalTool,
|
|
2726
|
+
execute: withToolErrorBoundary(
|
|
2727
|
+
terminalTool.name,
|
|
2728
|
+
async (...args) => await terminalTool.execute(...args, toolContext),
|
|
2729
|
+
),
|
|
2730
|
+
}),
|
|
2731
|
+
{ name: terminalTool.name },
|
|
2732
|
+
);
|
|
2733
|
+
continue;
|
|
2734
|
+
}
|
|
2735
|
+
api.registerTool({
|
|
2736
|
+
...terminalTool,
|
|
2737
|
+
execute: withToolErrorBoundary(terminalTool.name, terminalTool.execute),
|
|
2738
|
+
});
|
|
2603
2739
|
}
|
|
2604
2740
|
}
|
|
2605
2741
|
return plugin;
|
|
@@ -97,6 +97,8 @@ export function buildInboundEnvelope(message = {}) {
|
|
|
97
97
|
'notification',
|
|
98
98
|
'conversationKey',
|
|
99
99
|
'worldId',
|
|
100
|
+
'chatRequestId',
|
|
101
|
+
'requestId',
|
|
100
102
|
]) {
|
|
101
103
|
if (payload[key] == null && data[key] != null) payload[key] = data[key];
|
|
102
104
|
}
|
|
@@ -134,6 +136,17 @@ export function buildInboundEnvelope(message = {}) {
|
|
|
134
136
|
data.eventName,
|
|
135
137
|
normalizeEnvelopeText(payload.eventName, isDeliveryEvent ? null : normalizeEnvelopeText(message.event, null)),
|
|
136
138
|
);
|
|
139
|
+
const chatRequestId = [
|
|
140
|
+
data.chatRequestId,
|
|
141
|
+
data.requestId,
|
|
142
|
+
payload.chatRequestId,
|
|
143
|
+
payload.requestId,
|
|
144
|
+
metadata.kickoffRequestId,
|
|
145
|
+
metadata.chatRequestId,
|
|
146
|
+
metadata.requestId,
|
|
147
|
+
notification.chatRequestId,
|
|
148
|
+
notification.relatedObjects?.chatRequestId,
|
|
149
|
+
].map((candidate) => normalizeEnvelopeText(candidate, null)).find(Boolean) || null;
|
|
137
150
|
return {
|
|
138
151
|
eventType: eventType || 'delivery',
|
|
139
152
|
eventName,
|
|
@@ -141,6 +154,7 @@ export function buildInboundEnvelope(message = {}) {
|
|
|
141
154
|
deliveryId,
|
|
142
155
|
sessionKey,
|
|
143
156
|
targetAgentId,
|
|
157
|
+
chatRequestId,
|
|
144
158
|
conversationKey: normalizeEnvelopeText(
|
|
145
159
|
data.conversationKey,
|
|
146
160
|
normalizeEnvelopeText(payload.conversationKey, normalizeEnvelopeText(notification.relatedObjects?.conversationKey, null)),
|
|
@@ -15,18 +15,15 @@ export const CLAWORLD_WORLD_TOOL_NAMES = Object.freeze([
|
|
|
15
15
|
|
|
16
16
|
export const CLAWORLD_CONVERSATION_TOOL_NAMES = Object.freeze([
|
|
17
17
|
'claworld_manage_conversations',
|
|
18
|
-
]);
|
|
19
|
-
|
|
20
|
-
export const CLAWORLD_TRANSCRIPT_TOOL_NAMES = Object.freeze([
|
|
21
18
|
'claworld_render_transcript_report',
|
|
22
19
|
]);
|
|
23
20
|
|
|
21
|
+
|
|
24
22
|
export const CLAWORLD_REGISTERED_TOOL_NAMES = Object.freeze([
|
|
25
23
|
...CLAWORLD_ACCOUNT_TOOL_NAMES,
|
|
26
24
|
...CLAWORLD_SEARCH_TOOL_NAMES,
|
|
27
25
|
...CLAWORLD_WORLD_TOOL_NAMES,
|
|
28
26
|
...CLAWORLD_CONVERSATION_TOOL_NAMES,
|
|
29
|
-
...CLAWORLD_TRANSCRIPT_TOOL_NAMES,
|
|
30
27
|
]);
|
|
31
28
|
|
|
32
29
|
export const CLAWORLD_PUBLIC_TOOL_NAMES = Object.freeze([
|