@xfxstudio/claworld 2026.7.8-testing.1 → 2026.7.9-testing.1

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.
@@ -13,6 +13,7 @@ import {
13
13
  projectToolWorldSearchResponse,
14
14
  } from '../runtime/tool-contracts.js';
15
15
  import { CLAWORLD_TOOL_CONTRACT_VERSION } from '../runtime/tool-inventory.js';
16
+ import { CLAWORLD_PLUGIN_CURRENT_VERSION } from '../plugin-version.js';
16
17
  import {
17
18
  CLAWORLD_BOOTSTRAP_TARGETS,
18
19
  appendClaworldJournalEvent,
@@ -24,6 +25,12 @@ import {
24
25
  updateClaworldSessionDirectory,
25
26
  } from '../runtime/working-memory.js';
26
27
  import { resolveOpenClawWorkspaceRoot } from '../runtime/workspace-resolver.js';
28
+ import {
29
+ augmentConversationPayloadWithLocalTranscriptIndex,
30
+ CLAWORLD_TRANSCRIPT_REPORT_STYLE,
31
+ CLAWORLD_TRANSCRIPT_REPORT_TOOL_NAME,
32
+ renderClaworldTranscriptReport,
33
+ } from '../runtime/transcript-report.js';
27
34
  import { setClaworldRuntime } from './runtime.js';
28
35
  import { PUBLIC_TOOL_ACTION_CATALOG } from '../../product-shell/contracts/search-item.js';
29
36
  import {
@@ -145,6 +152,18 @@ const CHAT_INBOX_FILTER_STATUSES = Object.freeze([
145
152
  'kickoff_failed',
146
153
  'ended',
147
154
  ]);
155
+ const FEEDBACK_CATEGORIES = Object.freeze([
156
+ 'experience_issue',
157
+ 'usage_issue',
158
+ 'bug_report',
159
+ 'feature_request',
160
+ ]);
161
+ const FEEDBACK_IMPACTS = Object.freeze([
162
+ 'low',
163
+ 'medium',
164
+ 'high',
165
+ 'blocker',
166
+ ]);
148
167
  const CHAT_INBOX_FILTER_KEYS = Object.freeze([
149
168
  'direction',
150
169
  'mode',
@@ -177,6 +196,8 @@ const TERMINAL_ACCOUNT_ACTIONS = PUBLIC_TOOL_ACTION_CATALOG.claworld_manage_acco
177
196
  const TERMINAL_WORLD_ACTIONS = PUBLIC_TOOL_ACTION_CATALOG.claworld_manage_worlds;
178
197
  const TERMINAL_CONVERSATION_ACTIONS = PUBLIC_TOOL_ACTION_CATALOG.claworld_manage_conversations;
179
198
 
199
+ const CLAWORLD_WORLD_TOOL_GUIDANCE = 'Before any world operation, read the `claworld-manage-worlds` skill.';
200
+
180
201
  const ACCOUNT_IMPLEMENTATION_ACTIONS = Object.freeze({
181
202
  view_account: 'view',
182
203
  });
@@ -240,6 +261,13 @@ function normalizeTerminalAccountAction(params = {}) {
240
261
  requireManageWorldField('chatRequestPolicy', 'chatRequestPolicy is not supported by claworld_manage_account; use contactPolicy');
241
262
  }
242
263
  if (Object.prototype.hasOwnProperty.call(params, 'proactivitySettings')) return 'set_proactivity';
264
+ if (
265
+ hasProvidedToolParam(params, 'category')
266
+ || hasProvidedToolParam(params, 'title')
267
+ || hasProvidedToolParam(params, 'actualBehavior')
268
+ || hasProvidedToolParam(params, 'expectedBehavior')
269
+ || hasProvidedToolParam(params, 'reproductionSteps')
270
+ ) return 'submit_feedback';
243
271
  return 'view_account';
244
272
  }
245
273
 
@@ -348,6 +376,66 @@ function hasProvidedToolParam(params = {}, fieldId) {
348
376
  return value != null;
349
377
  }
350
378
 
379
+ function normalizeFeedbackStringList(values = []) {
380
+ const source = Array.isArray(values) ? values : [values];
381
+ return [...new Set(source.map((value) => normalizeText(value, null)).filter(Boolean))];
382
+ }
383
+
384
+ function validateTerminalFeedbackPayload(params = {}) {
385
+ const category = normalizeText(params.category, null);
386
+ const impact = normalizeText(params.impact, 'medium');
387
+ if (!category) requireManageWorldField('category', 'category is required for action=submit_feedback');
388
+ if (!FEEDBACK_CATEGORIES.includes(category)) {
389
+ requireManageWorldField('category', `category must be one of ${FEEDBACK_CATEGORIES.join(', ')}`);
390
+ }
391
+ if (!FEEDBACK_IMPACTS.includes(impact)) {
392
+ requireManageWorldField('impact', `impact must be one of ${FEEDBACK_IMPACTS.join(', ')}`);
393
+ }
394
+ for (const fieldId of ['title', 'goal', 'actualBehavior', 'expectedBehavior']) {
395
+ if (!normalizeText(params[fieldId], null)) {
396
+ requireManageWorldField(fieldId, `${fieldId} is required for action=submit_feedback`);
397
+ }
398
+ }
399
+ if (
400
+ params.context != null
401
+ && (!params.context || typeof params.context !== 'object' || Array.isArray(params.context))
402
+ ) {
403
+ requireManageWorldField('context', 'context must be an object for action=submit_feedback');
404
+ }
405
+ }
406
+
407
+ function normalizeTerminalFeedbackContext(params = {}) {
408
+ const context = normalizeObject(params.context, {}) || {};
409
+ const metadata = normalizeObject(context.metadata, {}) || {};
410
+ const redactionNotes = normalizeText(params.redactionNotes, null);
411
+ return {
412
+ worldId: normalizeText(context.worldId, normalizeText(params.worldId, null)),
413
+ conversationKey: normalizeText(context.conversationKey, normalizeText(params.conversationKey, null)),
414
+ turnId: normalizeText(context.turnId, normalizeText(params.turnId, null)),
415
+ deliveryId: normalizeText(context.deliveryId, normalizeText(params.deliveryId, null)),
416
+ targetAgentId: normalizeText(context.targetAgentId, normalizeText(params.targetAgentId, null)),
417
+ tags: normalizeFeedbackStringList(context.tags),
418
+ metadata: {
419
+ ...metadata,
420
+ ...(redactionNotes ? { redactionNotes } : {}),
421
+ },
422
+ };
423
+ }
424
+
425
+ function normalizeTerminalFeedbackPayload(params = {}) {
426
+ return {
427
+ category: normalizeText(params.category, null),
428
+ title: normalizeText(params.title, null),
429
+ goal: normalizeText(params.goal, null),
430
+ actualBehavior: normalizeText(params.actualBehavior, null),
431
+ expectedBehavior: normalizeText(params.expectedBehavior, null),
432
+ impact: normalizeText(params.impact, 'medium'),
433
+ details: normalizeText(params.details, null),
434
+ reproductionSteps: normalizeFeedbackStringList(params.reproductionSteps),
435
+ context: normalizeTerminalFeedbackContext(params),
436
+ };
437
+ }
438
+
351
439
  function buildChatInboxFiltersParam({ description, worldIdProperty } = {}) {
352
440
  return objectParam({
353
441
  description,
@@ -393,6 +481,100 @@ function buildChatInboxFiltersParam({ description, worldIdProperty } = {}) {
393
481
  });
394
482
  }
395
483
 
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
+
396
578
  function validateChatInboxFilterInput(filters = {}, action) {
397
579
  const source = normalizeObject(filters, {}) || {};
398
580
  for (const key of Object.keys(source)) {
@@ -498,6 +680,7 @@ function createTerminalToolAdapters(api, plugin, internalTools) {
498
680
  const searchTool = 'claworld_search';
499
681
  const manageWorldsTool = 'claworld_manage_worlds';
500
682
  const manageConversationsTool = 'claworld_manage_conversations';
683
+ const transcriptReportTool = CLAWORLD_TRANSCRIPT_REPORT_TOOL_NAME;
501
684
  const accountTool = 'claworld_manage_account';
502
685
  const publicProfileTool = 'claworld_get_public_profile';
503
686
 
@@ -505,7 +688,7 @@ function createTerminalToolAdapters(api, plugin, internalTools) {
505
688
  {
506
689
  name: accountTool,
507
690
  label: 'Claworld Manage Account',
508
- description: 'Terminal account surface for readiness, public identity, global profile, share-card generation, visibility, inbound contact policy, and email-based identity verification.',
691
+ description: 'Terminal account surface for readiness, public identity, global profile, share-card generation, visibility, inbound contact policy, notification/proactivity policy, email-based identity verification, and authenticated feedback submission.',
509
692
  metadata: buildToolMetadata({
510
693
  category: 'account',
511
694
  usageNotes: [
@@ -513,6 +696,7 @@ function createTerminalToolAdapters(api, plugin, internalTools) {
513
696
  'Use action=view_account for readiness; update_display_name, update_agent_profile, or set_contact_policy for common account mutations.',
514
697
  'Use start_email_verification with email + optional displayName to start email-based identity verification, then complete_email_verification with email + code to finish.',
515
698
  'Use subscribe_person or unsubscribe_person when a search/profile result exposes a person subscription target.',
699
+ 'Use submit_feedback for Claworld bugs, confusing behavior, missing capability, or feature requests. The tool handles auth internally.',
516
700
  ],
517
701
  }),
518
702
  parameters: objectParam({
@@ -523,7 +707,7 @@ function createTerminalToolAdapters(api, plugin, internalTools) {
523
707
  action: stringParam({
524
708
  description: 'Account action.',
525
709
  enumValues: TERMINAL_ACCOUNT_ACTIONS,
526
- examples: ['view_account', 'start_email_verification', 'update_display_name', 'set_contact_policy'],
710
+ examples: ['view_account', 'start_email_verification', 'update_display_name', 'set_contact_policy', 'submit_feedback'],
527
711
  }),
528
712
  displayName: stringParam({
529
713
  description: 'Public-facing display name for update_display_name or start_email_verification.',
@@ -589,6 +773,49 @@ function createTerminalToolAdapters(api, plugin, internalTools) {
589
773
  minLength: 1,
590
774
  examples: ['123456'],
591
775
  }),
776
+ category: stringParam({
777
+ description: 'Feedback category.',
778
+ enumValues: FEEDBACK_CATEGORIES,
779
+ examples: ['bug_report', 'feature_request'],
780
+ }),
781
+ title: stringParam({
782
+ description: 'Short developer-readable feedback title.',
783
+ minLength: 1,
784
+ examples: ['Feedback submission should use account tool auth'],
785
+ }),
786
+ goal: stringParam({
787
+ description: 'What the human was trying to do.',
788
+ minLength: 1,
789
+ }),
790
+ actualBehavior: stringParam({
791
+ description: 'What actually happened.',
792
+ minLength: 1,
793
+ }),
794
+ expectedBehavior: stringParam({
795
+ description: 'What should have happened.',
796
+ minLength: 1,
797
+ }),
798
+ impact: stringParam({
799
+ description: 'Feedback impact.',
800
+ enumValues: FEEDBACK_IMPACTS,
801
+ examples: ['medium'],
802
+ }),
803
+ details: stringParam({
804
+ description: 'Developer-facing feedback details.',
805
+ minLength: 1,
806
+ }),
807
+ reproductionSteps: arrayParam({
808
+ description: 'Repeatable feedback reproduction steps.',
809
+ items: stringParam({ minLength: 1 }),
810
+ }),
811
+ context: objectParam({
812
+ description: 'Lookup metadata, such as worldId, conversationKey, deliveryId, targetAgentId, tags, and metadata.',
813
+ additionalProperties: true,
814
+ }),
815
+ redactionNotes: stringParam({
816
+ description: 'Optional note about what was redacted before submit_feedback.',
817
+ minLength: 1,
818
+ }),
592
819
  },
593
820
  }),
594
821
  async execute(toolCallId, params = {}) {
@@ -648,6 +875,33 @@ function createTerminalToolAdapters(api, plugin, internalTools) {
648
875
  return buildTerminalActionResult({ tool: accountTool, action, payload });
649
876
  }
650
877
 
878
+ if (action === 'submit_feedback') {
879
+ validateTerminalFeedbackPayload(params);
880
+ const feedback = normalizeTerminalFeedbackPayload(params);
881
+ const toolContext = await resolveToolContext(api, plugin, params, {
882
+ requiredPublicIdentityCapability: 'submit feedback',
883
+ });
884
+ if (typeof plugin.runtime.productShell.feedback?.submitFeedback !== 'function') {
885
+ requireManageWorldField('action', 'action=submit_feedback requires the feedback runtime adapter');
886
+ }
887
+ const payload = await plugin.runtime.productShell.feedback.submitFeedback({
888
+ ...toolContext,
889
+ ...feedback,
890
+ toolCallId,
891
+ source: 'openclaw_account_tool',
892
+ runtimeToolName: accountTool,
893
+ accountToolAction: action,
894
+ pluginVersion: CLAWORLD_PLUGIN_CURRENT_VERSION,
895
+ toolContractVersion: CLAWORLD_TOOL_CONTRACT_VERSION,
896
+ });
897
+ return buildTerminalActionResult({
898
+ tool: accountTool,
899
+ action,
900
+ payload: projectToolFeedbackSubmissionResponse(payload),
901
+ status: 'recorded',
902
+ });
903
+ }
904
+
651
905
  const implementationAction = ACCOUNT_IMPLEMENTATION_ACTIONS[action] || null;
652
906
  if (implementationAction) {
653
907
  const implementationParams = {
@@ -892,7 +1146,7 @@ function createTerminalToolAdapters(api, plugin, internalTools) {
892
1146
  {
893
1147
  name: manageWorldsTool,
894
1148
  label: 'Claworld Manage Worlds',
895
- description: 'Terminal world surface for browsing selected world details, joining, creation, owner governance, broadcast, and membership self-service.',
1149
+ description: `Terminal world surface for browsing selected world details, joining, creation, governance, broadcast, invites, membership, subscriptions, and participation context. ${CLAWORLD_WORLD_TOOL_GUIDANCE}`,
896
1150
  metadata: buildToolMetadata({
897
1151
  category: 'world_management',
898
1152
  usageNotes: [
@@ -1214,7 +1468,15 @@ function createTerminalToolAdapters(api, plugin, internalTools) {
1214
1468
  action: 'list',
1215
1469
  ...(Object.keys(filters).length > 0 ? { filters } : {}),
1216
1470
  });
1217
- return rewriteToolResultName(result, manageConversationsTool, action);
1471
+ return augmentConversationToolResultWithTranscripts(
1472
+ api,
1473
+ plugin,
1474
+ params,
1475
+ filters,
1476
+ result,
1477
+ manageConversationsTool,
1478
+ action,
1479
+ );
1218
1480
  }
1219
1481
  if (action === 'accept' || action === 'reject') {
1220
1482
  const result = await requireTerminalTool(internalTools, 'claworld_chat_inbox').execute(toolCallId, {
@@ -1245,6 +1507,33 @@ function createTerminalToolAdapters(api, plugin, internalTools) {
1245
1507
  return buildToolResult({ status: 'error', tool: manageConversationsTool });
1246
1508
  },
1247
1509
  },
1510
+ {
1511
+ name: transcriptReportTool,
1512
+ label: 'Claworld Render Transcript Report',
1513
+ description: 'Render a local Claworld conversation transcript into BubbleSpec, SVG pages, and PNG pages. Prefer mode=stored with a chatRequestId from claworld_manage_conversations or .claworld/sessions/index.json; use manual mode only for selected excerpts or fallback rendering.',
1514
+ metadata: buildToolMetadata({
1515
+ category: 'transcript_report',
1516
+ usageNotes: [
1517
+ 'Use mode=stored when the exact chatRequestId is known and the local Conversation Session transcript has been indexed.',
1518
+ 'Use mode=manual only when the human asks for a selected excerpt or the stored transcript is unavailable.',
1519
+ 'Attach deliveryHint.primaryMedia or deliveryHint.primaryMediaBatch when reporting the visual transcript through a media-capable OpenClaw surface.',
1520
+ 'Do not paste raw transcript text to the human when this report rendering tool can produce the visual artifact.',
1521
+ ],
1522
+ }),
1523
+ parameters: buildTranscriptReportParameters(),
1524
+ async execute(_toolCallId, params = {}) {
1525
+ const { workspaceRoot, agentId } = await resolveLocalWorkspaceRootForTool(api, plugin, params);
1526
+ const payload = await renderClaworldTranscriptReport({
1527
+ workspaceRoot,
1528
+ agentId,
1529
+ args: params,
1530
+ });
1531
+ return buildToolResult({
1532
+ ...payload,
1533
+ tool: transcriptReportTool,
1534
+ });
1535
+ },
1536
+ },
1248
1537
  ];
1249
1538
  }
1250
1539
 
@@ -1303,7 +1592,7 @@ function buildRegisteredTools(api, plugin) {
1303
1592
  {
1304
1593
  name: 'claworld_get_world_detail',
1305
1594
  label: 'Claworld Get World Detail',
1306
- description: 'Canonical world-inspection tool. Fetch one world detail before deciding whether to join it.',
1595
+ description: `Canonical world-inspection tool. Fetch one world detail before deciding whether to join it. ${CLAWORLD_WORLD_TOOL_GUIDANCE}`,
1307
1596
  metadata: buildToolMetadata({
1308
1597
  category: 'world_discovery',
1309
1598
  usageNotes: [
@@ -1348,7 +1637,7 @@ function buildRegisteredTools(api, plugin) {
1348
1637
  {
1349
1638
  name: 'claworld_join_world',
1350
1639
  label: 'Claworld Join World',
1351
- description: 'Canonical world-entry tool. Submit one world-scoped participantContextText for the selected world and receive the current join result, membership state, and terminal member-discovery follow-up actions.',
1640
+ description: `Canonical world-entry tool. Submit one world-scoped participantContextText for the selected world and receive the current join result, membership state, and terminal member-discovery follow-up actions. ${CLAWORLD_WORLD_TOOL_GUIDANCE}`,
1352
1641
  metadata: buildToolMetadata({
1353
1642
  category: 'world_join',
1354
1643
  usageNotes: [
@@ -1406,7 +1695,7 @@ function buildRegisteredTools(api, plugin) {
1406
1695
  {
1407
1696
  name: 'claworld_create_world',
1408
1697
  label: 'Claworld Create World',
1409
- 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.',
1698
+ description: `Creator/admin entrypoint for publishing one new managed world. It also accepts the creator participantContextText and returns the self-join result block on success. ${CLAWORLD_WORLD_TOOL_GUIDANCE}`,
1410
1699
  metadata: buildToolMetadata({
1411
1700
  category: 'world_creation',
1412
1701
  usageNotes: [
@@ -1494,7 +1783,7 @@ function buildRegisteredTools(api, plugin) {
1494
1783
  {
1495
1784
  name: 'claworld_manage_world',
1496
1785
  label: 'Claworld Manage World',
1497
- 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.',
1786
+ description: `Unified world management tool. Use governance actions for world management, or member actions to inspect joined worlds, update your world profile, and leave a world. ${CLAWORLD_WORLD_TOOL_GUIDANCE}`,
1498
1787
  metadata: buildToolMetadata({
1499
1788
  category: 'world_management',
1500
1789
  usageNotes: [
@@ -1910,7 +2199,7 @@ function buildRegisteredTools(api, plugin) {
1910
2199
  {
1911
2200
  name: 'claworld_account',
1912
2201
  label: 'Claworld Account',
1913
- description: 'Canonical account surface. View current relay binding plus public identity readiness, or update the public display identity for the current Claworld account.',
2202
+ description: 'Canonical account surface. View current relay binding plus public identity readiness, or update the public display identity/profile for the current Claworld account.',
1914
2203
  metadata: buildToolMetadata({
1915
2204
  category: 'account',
1916
2205
  usageNotes: [
@@ -37,6 +37,9 @@ export async function submitFeedbackReport({
37
37
  fetchImpl,
38
38
  logger = console,
39
39
  toolCallId = null,
40
+ source = 'openclaw_runtime',
41
+ runtimeToolName = 'claworld_feedback_helper',
42
+ accountToolAction = null,
40
43
  pluginVersion = null,
41
44
  toolContractVersion = null,
42
45
  } = {}) {
@@ -86,10 +89,11 @@ export async function submitFeedbackReport({
86
89
  tags: normalizeStringList(normalizedContext.tags),
87
90
  metadata: normalizeObject(normalizedContext.metadata),
88
91
  },
89
- source: 'openclaw_runtime',
92
+ source: normalizeText(source, 'openclaw_runtime'),
90
93
  runtimeContext: {
91
94
  channelId: 'claworld',
92
- toolName: 'claworld_feedback_helper',
95
+ toolName: normalizeText(runtimeToolName, 'claworld_feedback_helper'),
96
+ accountToolAction: normalizeText(accountToolAction, null),
93
97
  toolCallId: normalizeText(toolCallId, null),
94
98
  ...diagnostics,
95
99
  toolContractVersion: normalizeText(toolContractVersion, null),
@@ -565,6 +565,7 @@ export function projectToolFeedbackSubmissionResponse(result = {}) {
565
565
  runtime: {
566
566
  channelId: normalizeText(runtimeContext.channelId, null),
567
567
  toolName: normalizeText(runtimeContext.toolName, null),
568
+ accountToolAction: normalizeText(runtimeContext.accountToolAction, null),
568
569
  toolCallId: normalizeText(runtimeContext.toolCallId, null),
569
570
  openclawVersion: normalizeText(runtimeContext.openclawVersion, null),
570
571
  pluginVersion: normalizeText(runtimeContext.pluginVersion, null),
@@ -17,12 +17,16 @@ export const CLAWORLD_CONVERSATION_TOOL_NAMES = Object.freeze([
17
17
  'claworld_manage_conversations',
18
18
  ]);
19
19
 
20
+ export const CLAWORLD_TRANSCRIPT_TOOL_NAMES = Object.freeze([
21
+ 'claworld_render_transcript_report',
22
+ ]);
20
23
 
21
24
  export const CLAWORLD_REGISTERED_TOOL_NAMES = Object.freeze([
22
25
  ...CLAWORLD_ACCOUNT_TOOL_NAMES,
23
26
  ...CLAWORLD_SEARCH_TOOL_NAMES,
24
27
  ...CLAWORLD_WORLD_TOOL_NAMES,
25
28
  ...CLAWORLD_CONVERSATION_TOOL_NAMES,
29
+ ...CLAWORLD_TRANSCRIPT_TOOL_NAMES,
26
30
  ]);
27
31
 
28
32
  export const CLAWORLD_PUBLIC_TOOL_NAMES = Object.freeze([