@xfxstudio/claworld 2026.6.5 → 2026.6.10-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.
@@ -247,7 +247,7 @@ async function applyManagedOnboardingConfig({
247
247
  `Remote backend: ${managedOptions.serverUrl}`,
248
248
  managedOptions.appToken
249
249
  ? 'Activation state: ready via configured appToken'
250
- : 'Activation state: pending until claworld_manage_account(action=update_display_name) runs',
250
+ : 'Activation state: pending until claworld_manage_account(action=activate_account) runs',
251
251
  'This flow refreshes plugin-side config and binds claworld onto the selected local agent. It does not run installer commands or start a backend service.',
252
252
  ];
253
253
  await prompter.note(
@@ -20,6 +20,7 @@ import {
20
20
  } from '../runtime/backend-error-context.js';
21
21
 
22
22
  export const INTERNAL_REQUESTER_SESSION_KEY_PARAM = '__claworldRequesterSessionKey';
23
+ export const INTERNAL_WORKSPACE_ROOT_PARAM = '__claworldWorkspaceRoot';
23
24
 
24
25
  export function normalizeText(value, fallback = null) {
25
26
  if (value == null) return fallback;
@@ -240,9 +241,10 @@ export async function resolveToolContext(
240
241
  runtime: api?.runtime || null,
241
242
  accountId,
242
243
  runtimeConfig,
243
- agentId: normalizeText(params.agentId, runtimeConfig.relay?.agentId || null),
244
- requesterSessionKey: normalizeText(params[INTERNAL_REQUESTER_SESSION_KEY_PARAM], null),
245
- });
244
+ agentId: normalizeText(params.agentId, runtimeConfig.relay?.agentId || null),
245
+ requesterSessionKey: normalizeText(params[INTERNAL_REQUESTER_SESSION_KEY_PARAM], null),
246
+ workspaceRoot: normalizeText(params[INTERNAL_WORKSPACE_ROOT_PARAM], null),
247
+ });
246
248
  if (
247
249
  requiredPublicIdentityCapability
248
250
  && (
@@ -282,6 +284,7 @@ export async function resolveToolContext(
282
284
  runtimeConfig,
283
285
  agentId,
284
286
  requesterSessionKey: normalizeText(params[INTERNAL_REQUESTER_SESSION_KEY_PARAM], null),
287
+ workspaceRoot: normalizeText(params[INTERNAL_WORKSPACE_ROOT_PARAM], null),
285
288
  };
286
289
  }
287
290
 
@@ -2,6 +2,10 @@ import {
2
2
  createClaworldChannelPlugin,
3
3
  recordClaworldRuntimeAssistantOutput,
4
4
  } from './claworld-channel-plugin.js';
5
+ import {
6
+ attachConversationViewerToPayload,
7
+ buildConversationViewerRoute,
8
+ } from './conversation-viewer.js';
5
9
  import {
6
10
  projectToolChatRequestMutationResponse,
7
11
  projectToolCreateWorldResponse,
@@ -41,6 +45,7 @@ import {
41
45
  inferChatInboxAction,
42
46
  inferManageWorldAction,
43
47
  INTERNAL_REQUESTER_SESSION_KEY_PARAM,
48
+ INTERNAL_WORKSPACE_ROOT_PARAM,
44
49
  integerParam,
45
50
  loadCurrentConfig,
46
51
  MANAGE_WORLD_ACTIONS,
@@ -267,6 +272,24 @@ function buildTerminalActionResult({ tool, action, payload = {}, status = null }
267
272
  });
268
273
  }
269
274
 
275
+ async function attachLocalConversationViewer(payload, {
276
+ context = {},
277
+ params = {},
278
+ role = null,
279
+ source = null,
280
+ logger = console,
281
+ } = {}) {
282
+ return await attachConversationViewerToPayload(payload, {
283
+ accountId: context.accountId || null,
284
+ viewerAgentId: context.agentId || null,
285
+ role,
286
+ source,
287
+ cfg: context.cfg || {},
288
+ workspaceRoot: context.workspaceRoot || params[INTERNAL_WORKSPACE_ROOT_PARAM] || null,
289
+ logger,
290
+ });
291
+ }
292
+
270
293
  function normalizeChatInboxListFiltersInput(params = {}) {
271
294
  const source = normalizeObject(params.filters, {}) || {};
272
295
  const normalized = {
@@ -1093,10 +1116,17 @@ function createTerminalToolAdapters(api, plugin, internalTools) {
1093
1116
  conversationKey,
1094
1117
  localSessionKey,
1095
1118
  });
1119
+ const payloadWithViewer = await attachLocalConversationViewer(payload, {
1120
+ context,
1121
+ params,
1122
+ role: 'participant',
1123
+ source: 'close_conversation',
1124
+ logger: getHookLogger(api),
1125
+ });
1096
1126
  return buildTerminalActionResult({
1097
1127
  tool: manageConversationsTool,
1098
1128
  action,
1099
- payload,
1129
+ payload: payloadWithViewer,
1100
1130
  });
1101
1131
  }
1102
1132
  requireManageWorldField('action', `action must be one of ${TERMINAL_CONVERSATION_ACTIONS.join(', ')}`);
@@ -1691,6 +1721,7 @@ function buildRegisteredTools(api, plugin) {
1691
1721
  'Do not use this tool for replying inside an already-open Claworld chat or for runtime live turns.',
1692
1722
  'After creation, use claworld_chat_inbox to inspect pending, expired, rejected, opening, ending, active, silent, or ended status, or wait for the peer to accept.',
1693
1723
  'Once accepted, the runtime owns the live conversation loop.',
1724
+ 'If the result includes conversationViewer.url, include that local viewer link in the owner-facing response so the human can watch the chat live.',
1694
1725
  ],
1695
1726
  examples: [
1696
1727
  {
@@ -1759,7 +1790,14 @@ function buildRegisteredTools(api, plugin) {
1759
1790
  openingMessage: params.openingMessage || null,
1760
1791
  worldId: params.worldId || null,
1761
1792
  });
1762
- return buildToolResult(projectToolChatRequestMutationResponse(payload, { accountId: context.accountId }));
1793
+ const payloadWithViewer = await attachLocalConversationViewer(payload, {
1794
+ context,
1795
+ params,
1796
+ role: 'requester',
1797
+ source: 'request_chat',
1798
+ logger: getHookLogger(api),
1799
+ });
1800
+ return buildToolResult(projectToolChatRequestMutationResponse(payloadWithViewer, { accountId: context.accountId }));
1763
1801
  },
1764
1802
  },
1765
1803
  {
@@ -1778,6 +1816,7 @@ function buildRegisteredTools(api, plugin) {
1778
1816
  'For user requests to contact, PK, continue, or re-engage a Claworld peer, use claworld_manage_conversations(action=request) with the intended direct or world scope.',
1779
1817
  'Peer-facing opener/reply/final content is delivered by the Conversation Session and backend conversation runtime. Main Session must not use sessions_send to write peer-facing content into a local conversation session.',
1780
1818
  'Prefer Claworld conversation state, reports, and concise summaries before inspecting raw local transcript details.',
1819
+ 'If an accept result includes conversationViewer.url, include that local viewer link in the owner-facing response or report.',
1781
1820
  'Global counts stay visible even when filters are applied; filtered counts describe the current narrowed result set.',
1782
1821
  'After action=accept or action=reject, call action=list again to refresh the inbox view.',
1783
1822
  ],
@@ -1868,7 +1907,16 @@ function buildRegisteredTools(api, plugin) {
1868
1907
  ...context,
1869
1908
  chatRequestId,
1870
1909
  });
1871
- return buildToolResult(projectToolChatInboxActionResponse(payload, {
1910
+ const payloadWithViewer = action === 'accept'
1911
+ ? await attachLocalConversationViewer(payload, {
1912
+ context,
1913
+ params,
1914
+ role: 'recipient',
1915
+ source: 'accept_chat_request',
1916
+ logger: getHookLogger(api),
1917
+ })
1918
+ : payload;
1919
+ return buildToolResult(projectToolChatInboxActionResponse(payloadWithViewer, {
1872
1920
  accountId: context.accountId,
1873
1921
  action,
1874
1922
  }));
@@ -1956,7 +2004,7 @@ function buildRegisteredTools(api, plugin) {
1956
2004
  examples: ['Moza', '小发发'],
1957
2005
  }),
1958
2006
  profile: stringParam({
1959
- description: 'Global plain-text profile for this account. Maximum 200 characters. Use an empty string to clear it. HTML is not supported.',
2007
+ description: 'Global plain-text profile for this account. Maximum 500 characters. Use an empty string to clear it. HTML is not supported.',
1960
2008
  examples: ['喜欢慢节奏介绍和小范围世界,也愿意先让 agent 帮我做初步认识。🙂'],
1961
2009
  }),
1962
2010
  chatRequestApprovalPolicy: chatRequestApprovalPolicyProperty,
@@ -2148,7 +2196,7 @@ function buildRegisteredTools(api, plugin) {
2148
2196
  }));
2149
2197
  }
2150
2198
 
2151
- export function registerClaworldPluginFull(api, plugin) {
2199
+ export function registerClaworldPluginFull(api, plugin, { fetchImpl = globalThis.fetch } = {}) {
2152
2200
  if (!plugin) {
2153
2201
  throw new Error('registerClaworldPluginFull requires a plugin instance');
2154
2202
  }
@@ -2217,17 +2265,16 @@ export function registerClaworldPluginFull(api, plugin) {
2217
2265
  ? event.params
2218
2266
  : {};
2219
2267
  const requesterSessionKey = normalizeText(ctx?.sessionKey, null);
2220
- if (
2221
- toolName !== 'claworld_manage_conversations'
2222
- || normalizeTerminalConversationAction(params.action, null) !== 'request'
2223
- || !requesterSessionKey
2224
- ) {
2225
- return;
2226
- }
2268
+ if (toolName !== 'claworld_manage_conversations') return;
2227
2269
  const logger = getHookLogger(api);
2270
+ let workspaceRoot = null;
2228
2271
  try {
2229
- const workspaceRoot = await resolveHookWorkspaceRoot(api, event, ctx);
2230
- if (workspaceRoot) {
2272
+ workspaceRoot = await resolveHookWorkspaceRoot(api, event, ctx);
2273
+ if (
2274
+ workspaceRoot
2275
+ && requesterSessionKey
2276
+ && normalizeTerminalConversationAction(params.action, null) === 'request'
2277
+ ) {
2231
2278
  await updateClaworldSessionDirectory(
2232
2279
  workspaceRoot,
2233
2280
  {
@@ -2248,10 +2295,12 @@ export function registerClaworldPluginFull(api, plugin) {
2248
2295
  } catch (error) {
2249
2296
  logger?.warn?.('[claworld:working-memory] unable to update requester session directory', error);
2250
2297
  }
2298
+ if (!requesterSessionKey && !workspaceRoot) return;
2251
2299
  return {
2252
2300
  params: {
2253
2301
  ...params,
2254
- [INTERNAL_REQUESTER_SESSION_KEY_PARAM]: requesterSessionKey,
2302
+ ...(requesterSessionKey ? { [INTERNAL_REQUESTER_SESSION_KEY_PARAM]: requesterSessionKey } : {}),
2303
+ ...(workspaceRoot ? { [INTERNAL_WORKSPACE_ROOT_PARAM]: workspaceRoot } : {}),
2255
2304
  },
2256
2305
  };
2257
2306
  });
@@ -2280,6 +2329,11 @@ export function registerClaworldPluginFull(api, plugin) {
2280
2329
  }
2281
2330
  if (typeof api.registerHttpRoute === 'function') {
2282
2331
  api.registerHttpRoute(buildClaworldStatusRoute(plugin));
2332
+ api.registerHttpRoute(buildConversationViewerRoute(plugin, {
2333
+ api,
2334
+ fetchImpl,
2335
+ logger: getHookLogger(api),
2336
+ }));
2283
2337
  }
2284
2338
  if (typeof api.registerTool === 'function') {
2285
2339
  const internalTools = new Map(
@@ -2310,7 +2364,7 @@ export function registerClaworldPlugin(api, options = {}) {
2310
2364
  } = options;
2311
2365
  const plugin = existingPlugin || createClaworldChannelPlugin(pluginOptions);
2312
2366
  api.registerChannel({ plugin });
2313
- registerClaworldPluginFull(api, plugin);
2367
+ registerClaworldPluginFull(api, plugin, pluginOptions);
2314
2368
  return plugin;
2315
2369
  }
2316
2370
 
@@ -641,6 +641,35 @@ function projectChatRequestOrigin(origin = {}) {
641
641
  };
642
642
  }
643
643
 
644
+ function projectConversationViewer(viewer = null) {
645
+ if (!viewer || typeof viewer !== 'object' || Array.isArray(viewer)) return null;
646
+ const id = normalizeText(viewer.id, normalizeText(viewer.viewerId, null));
647
+ const url = normalizeText(viewer.url, null);
648
+ if (!id && !url && normalizeText(viewer.status, null) !== 'unavailable') return null;
649
+ return {
650
+ id,
651
+ status: normalizeText(viewer.status, 'ready'),
652
+ url,
653
+ localFile: normalizeText(viewer.localFile, null),
654
+ fileUrl: normalizeText(viewer.fileUrl, null),
655
+ chatRequestId: normalizeText(viewer.chatRequestId, null),
656
+ conversationKey: normalizeText(viewer.conversationKey, null),
657
+ localSessionKey: normalizeText(viewer.localSessionKey, null),
658
+ replay: viewer.replay && typeof viewer.replay === 'object' && !Array.isArray(viewer.replay)
659
+ ? {
660
+ firstOpenReplaysEndedConversation: typeof viewer.replay.firstOpenReplaysEndedConversation === 'boolean'
661
+ ? viewer.replay.firstOpenReplaysEndedConversation
662
+ : null,
663
+ secondOpenShowsFullTranscript: typeof viewer.replay.secondOpenShowsFullTranscript === 'boolean'
664
+ ? viewer.replay.secondOpenShowsFullTranscript
665
+ : null,
666
+ }
667
+ : null,
668
+ note: normalizeText(viewer.note, null),
669
+ reason: normalizeText(viewer.reason, null),
670
+ };
671
+ }
672
+
644
673
  function projectChatRequestItem(request = {}) {
645
674
  if (!request || typeof request !== 'object') return null;
646
675
  const requestContext = request.requestContext && typeof request.requestContext === 'object' && !Array.isArray(request.requestContext)
@@ -656,6 +685,7 @@ function projectChatRequestItem(request = {}) {
656
685
  ? request.conversation
657
686
  : requestContext.conversation,
658
687
  );
688
+ const conversationViewer = projectConversationViewer(request.conversationViewer);
659
689
  return {
660
690
  chatRequestId: normalizeText(request.chatRequestId || request.requestId, null),
661
691
  status: normalizeText(request.status, 'pending'),
@@ -681,11 +711,13 @@ function projectChatRequestItem(request = {}) {
681
711
  toAgent: projectToolAgentSummary(request.toAgent),
682
712
  counterparty: projectToolAgentSummary(request.counterparty),
683
713
  conversation,
714
+ ...(conversationViewer ? { conversationViewer } : {}),
684
715
  };
685
716
  }
686
717
 
687
718
  function projectChatInboxChatItem(chat = {}) {
688
719
  if (!chat || typeof chat !== 'object' || Array.isArray(chat)) return null;
720
+ const conversationViewer = projectConversationViewer(chat.conversationViewer);
689
721
  return {
690
722
  chatRequestId: normalizeText(chat.chatRequestId, null),
691
723
  status: normalizeText(chat.status, null),
@@ -699,6 +731,7 @@ function projectChatInboxChatItem(chat = {}) {
699
731
  counterparty: projectToolAgentSummary(chat.counterparty),
700
732
  conversation: normalizeConversationScopeDetails(chat.conversation),
701
733
  feedbackSummary: projectConversationFeedbackSummary(chat.feedbackSummary),
734
+ ...(conversationViewer ? { conversationViewer } : {}),
702
735
  };
703
736
  }
704
737
 
@@ -764,12 +797,19 @@ export function projectToolChatRequestMutationResponse(result = {}, { accountId
764
797
  const projectedRequest = projectChatRequestItem(request);
765
798
  const kickoff = projectChatRequestKickoff(result.kickoff || result.request?.kickoff);
766
799
  const projectedChat = projectChatInboxChatItem(result.chat);
800
+ const conversationViewer = projectConversationViewer(
801
+ result.conversationViewer
802
+ || request?.conversationViewer
803
+ || result.request?.conversationViewer
804
+ || result.chat?.conversationViewer,
805
+ );
767
806
  const normalizedStatus = normalizeText(result.status, projectedRequest?.status || 'pending');
768
807
  return {
769
808
  status: normalizedStatus,
770
809
  accountId: normalizeText(accountId, null),
771
810
  chatRequest: projectedRequest,
772
811
  ...(projectedChat ? { chat: projectedChat } : {}),
812
+ ...(conversationViewer ? { conversationViewer } : {}),
773
813
  kickoff,
774
814
  nextAction: normalizeText(
775
815
  result.nextAction,