@xfxstudio/claworld 2026.4.30-testing.1 → 2026.4.30-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.
@@ -8,7 +8,7 @@
8
8
  ],
9
9
  "name": "Claworld Persona Relay",
10
10
  "description": "Claworld relay world channel plugin for OpenClaw.",
11
- "version": "2026.4.30-testing.1",
11
+ "version": "2026.4.30-testing.2",
12
12
  "configSchema": {
13
13
  "type": "object",
14
14
  "additionalProperties": false,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xfxstudio/claworld",
3
- "version": "2026.4.30-testing.1",
3
+ "version": "2026.4.30-testing.2",
4
4
  "description": "Claworld channel plugin for OpenClaw",
5
5
  "type": "module",
6
6
  "main": "index.js",
@@ -31,9 +31,12 @@ description: |
31
31
  - That document is internal. It is for you only. Do not quote it, paraphrase
32
32
  it, summarize it to the peer, or mention its section names.
33
33
  - If workspace-local Claworld working memory is available and the current
34
- intent needs prior Claworld progress, read `.claworld/INDEX.md` first and
35
- follow its read order. Prefer summarized `NOW`, `MEMORY`, `PROFILE`, journal,
36
- and report content over raw transcripts.
34
+ intent needs prior Claworld progress, prefer direct reads from
35
+ `.claworld/context/NOW.md`, `.claworld/context/MEMORY.md`,
36
+ `.claworld/context/PROFILE.md`, `.claworld/journal/`, `.claworld/reports/`,
37
+ and `.claworld/sessions/index.json`. Use the session directory before
38
+ searching raw local session files. Prefer summarized `NOW`, `MEMORY`,
39
+ `PROFILE`, journal, and report content over raw transcripts.
37
40
  - Do not write `.claworld/` content into global `MEMORY.md`, and do not load
38
41
  raw Claworld transcripts by default.
39
42
  - A single local session transcript may contain multiple accepted-chat intents
@@ -32,6 +32,11 @@ import { createInboundSessionRouter } from '../runtime/inbound-session-router.js
32
32
  import { createOutboundSessionBridge } from '../runtime/outbound-session-bridge.js';
33
33
  import { createCanonicalResultBuilder } from '../runtime/canonical-result-builder.js';
34
34
  import { createDemoSessionBootstrap } from '../runtime/demo-session-bootstrap.js';
35
+ import {
36
+ appendClaworldJournalEvent,
37
+ buildClaworldRuntimeMaintenanceEvent,
38
+ } from '../runtime/working-memory.js';
39
+ import { resolveOpenClawWorkspaceRoot } from '../runtime/workspace-resolver.js';
35
40
  import {
36
41
  broadcastModeratedWorld,
37
42
  createModeratedWorld,
@@ -1807,6 +1812,230 @@ function buildDeliveryInboundEnvelope({
1807
1812
  };
1808
1813
  }
1809
1814
 
1815
+ function resolveRuntimeManagementEnvelope(event = {}) {
1816
+ const envelope = event?.delivery && typeof event.delivery === 'object' && !Array.isArray(event.delivery)
1817
+ ? event.delivery
1818
+ : {};
1819
+ const payload = envelope.payload && typeof envelope.payload === 'object' && !Array.isArray(envelope.payload)
1820
+ ? envelope.payload
1821
+ : {};
1822
+ const notification = payload.notification && typeof payload.notification === 'object' && !Array.isArray(payload.notification)
1823
+ ? payload.notification
1824
+ : {};
1825
+ return {
1826
+ envelope,
1827
+ payload,
1828
+ notification,
1829
+ eventType: resolveNormalizedText(envelope.eventType || payload.eventType || event.eventType, null),
1830
+ eventName: resolveNormalizedText(envelope.eventName || payload.eventName || notification.notificationType, null),
1831
+ eventId: resolveNormalizedText(
1832
+ envelope.eventId,
1833
+ resolveNormalizedText(payload.inboxItemId, resolveNormalizedText(notification.notificationId, null)),
1834
+ ),
1835
+ sessionKey: resolveNormalizedText(envelope.sessionKey, resolveNormalizedText(payload.sessionKey, null)),
1836
+ targetAgentId: resolveNormalizedText(
1837
+ envelope.targetAgentId,
1838
+ resolveNormalizedText(payload.targetAgentId, resolveNormalizedText(notification.targetAgentId, null)),
1839
+ ),
1840
+ worldId: resolveNormalizedText(envelope.worldId, resolveNormalizedText(payload.worldId, resolveNormalizedText(notification.relatedObjects?.worldId, null))),
1841
+ conversationKey: resolveNormalizedText(
1842
+ envelope.conversationKey,
1843
+ resolveNormalizedText(payload.conversationKey, resolveNormalizedText(notification.relatedObjects?.conversationKey, null)),
1844
+ ),
1845
+ createdAt: envelope.createdAt || payload.createdAt || notification.createdAt || null,
1846
+ };
1847
+ }
1848
+
1849
+ function stableJsonPreview(value = null, maxChars = 1200) {
1850
+ if (!value || typeof value !== 'object') return null;
1851
+ let json;
1852
+ try {
1853
+ json = JSON.stringify(value, null, 2);
1854
+ } catch {
1855
+ return null;
1856
+ }
1857
+ if (json.length <= maxChars) return json;
1858
+ return `${json.slice(0, Math.max(0, maxChars - 3))}...`;
1859
+ }
1860
+
1861
+ function buildManagementRuntimeEventBody(details = {}, { localSessionKey = null } = {}) {
1862
+ const notification = details.notification || {};
1863
+ const relatedObjects = notification.relatedObjects || details.payload?.relatedObjects || null;
1864
+ const nextActions = notification.nextActions || details.payload?.nextActions || null;
1865
+ const lines = [
1866
+ 'Claworld management event received.',
1867
+ '',
1868
+ `Event type: ${details.eventType || 'unknown'}`,
1869
+ `Event name: ${details.eventName || details.eventType || 'unknown'}`,
1870
+ details.eventId ? `Event id: ${details.eventId}` : null,
1871
+ `Management session: ${localSessionKey || details.sessionKey || 'unknown'}`,
1872
+ details.targetAgentId ? `Target agent: ${details.targetAgentId}` : null,
1873
+ details.worldId ? `World: ${details.worldId}` : null,
1874
+ details.conversationKey ? `Conversation: ${details.conversationKey}` : null,
1875
+ '',
1876
+ notification.notificationType ? `Notification type: ${notification.notificationType}` : null,
1877
+ notification.title ? `Title: ${notification.title}` : null,
1878
+ notification.body ? `Body: ${notification.body}` : null,
1879
+ notification.whyReceived ? `Why received: ${notification.whyReceived}` : null,
1880
+ relatedObjects ? 'Related objects:' : null,
1881
+ relatedObjects ? stableJsonPreview(relatedObjects, 900) : null,
1882
+ Array.isArray(nextActions) && nextActions.length > 0 ? 'Next actions:' : null,
1883
+ Array.isArray(nextActions) && nextActions.length > 0 ? stableJsonPreview(nextActions, 900) : null,
1884
+ '',
1885
+ 'Handle this as a Claworld Management Session input: decide whether to record, digest, act, report, or ask the user according to the current Claworld working memory and proactivity settings.',
1886
+ ].filter((line) => line != null && line !== '');
1887
+ return lines.join('\n');
1888
+ }
1889
+
1890
+ function resolveSessionRecordArtifacts(record = null, fallbackStorePath = null) {
1891
+ const normalizedRecord = record && typeof record === 'object' && !Array.isArray(record) ? record : {};
1892
+ const nestedSession = normalizedRecord.session && typeof normalizedRecord.session === 'object' && !Array.isArray(normalizedRecord.session)
1893
+ ? normalizedRecord.session
1894
+ : {};
1895
+ return {
1896
+ sessionId: resolveNormalizedText(
1897
+ normalizedRecord.sessionId,
1898
+ resolveNormalizedText(nestedSession.sessionId, resolveNormalizedText(normalizedRecord.id, null)),
1899
+ ),
1900
+ sessionFile: resolveNormalizedText(
1901
+ normalizedRecord.sessionFile,
1902
+ resolveNormalizedText(
1903
+ normalizedRecord.sessionPath,
1904
+ resolveNormalizedText(
1905
+ normalizedRecord.filePath,
1906
+ resolveNormalizedText(normalizedRecord.path, resolveNormalizedText(nestedSession.filePath, null)),
1907
+ ),
1908
+ ),
1909
+ ),
1910
+ sessionStorePath: resolveNormalizedText(
1911
+ normalizedRecord.storePath,
1912
+ resolveNormalizedText(normalizedRecord.sessionStorePath, fallbackStorePath),
1913
+ ),
1914
+ transcriptPath: resolveNormalizedText(
1915
+ normalizedRecord.transcriptPath,
1916
+ resolveNormalizedText(nestedSession.transcriptPath, null),
1917
+ ),
1918
+ };
1919
+ }
1920
+
1921
+ function buildDeliveryRuntimeMaintenanceEvent({
1922
+ delivery = {},
1923
+ metadata = {},
1924
+ payload = {},
1925
+ localSessionKey = null,
1926
+ localAgentId = null,
1927
+ sessionArtifacts = {},
1928
+ workspaceRoot = null,
1929
+ } = {}) {
1930
+ const deliveryId = resolveNormalizedText(delivery.deliveryId, null);
1931
+ const sessionKey = resolveNormalizedText(delivery.sessionKey, null);
1932
+ const requestId = resolveNormalizedText(
1933
+ metadata.kickoffRequestId,
1934
+ resolveNormalizedText(metadata.requestId, resolveNormalizedText(metadata.chatRequestId, null)),
1935
+ );
1936
+ const worldId = resolveNormalizedText(
1937
+ metadata.worldId,
1938
+ resolveNormalizedText(delivery.worldId, null),
1939
+ );
1940
+ const conversationKey = resolveNormalizedText(metadata.conversationKey, resolveNormalizedText(delivery.conversationKey, null));
1941
+ const fromAgentId = resolveNormalizedText(metadata.fromAgentId, null);
1942
+ const summary = [
1943
+ 'Inbound Claworld delivery joined local session',
1944
+ requestId ? `for request ${requestId}` : null,
1945
+ fromAgentId ? `from ${fromAgentId}` : null,
1946
+ ].filter(Boolean).join(' ');
1947
+ return buildClaworldRuntimeMaintenanceEvent({
1948
+ id: deliveryId ? `runtime:delivery:${deliveryId}` : null,
1949
+ timestamp: delivery.createdAt || metadata.createdAt || payload.createdAt || null,
1950
+ kind: metadata.deliveryType ? `delivery.${metadata.deliveryType}` : 'delivery',
1951
+ eventType: 'delivery',
1952
+ scope: 'conversation',
1953
+ summary,
1954
+ excerpt: payload.contextText
1955
+ ? 'Inbound delivery included contextText; raw dialogue is kept in the OpenClaw session transcript.'
1956
+ : 'Inbound delivery routed into an OpenClaw conversation session; raw dialogue is kept in the session transcript.',
1957
+ refs: {
1958
+ deliveryId,
1959
+ requestId,
1960
+ chatRequestId: requestId,
1961
+ worldId,
1962
+ conversationKey,
1963
+ fromAgentId,
1964
+ sessionKey: localSessionKey || sessionKey,
1965
+ relaySessionKey: sessionKey,
1966
+ },
1967
+ relations: {
1968
+ deliveryId,
1969
+ requestId,
1970
+ chatRequestId: requestId,
1971
+ worldId,
1972
+ conversationKey,
1973
+ fromAgentId,
1974
+ localAgentId,
1975
+ localSessionKey,
1976
+ relaySessionKey: sessionKey,
1977
+ sessionKey: localSessionKey || sessionKey,
1978
+ sessionId: sessionArtifacts.sessionId,
1979
+ sessionFile: sessionArtifacts.sessionFile,
1980
+ sessionStorePath: sessionArtifacts.sessionStorePath,
1981
+ transcriptPath: sessionArtifacts.transcriptPath,
1982
+ },
1983
+ artifacts: {
1984
+ workspaceRoot,
1985
+ ...sessionArtifacts,
1986
+ },
1987
+ });
1988
+ }
1989
+
1990
+ function buildManagementRuntimeMaintenanceEvent(details = {}, {
1991
+ localSessionKey = null,
1992
+ localAgentId = null,
1993
+ sessionArtifacts = {},
1994
+ workspaceRoot = null,
1995
+ } = {}) {
1996
+ const notification = details.notification || {};
1997
+ const refs = {
1998
+ inboxItemId: details.payload?.inboxItemId || details.eventId,
1999
+ notificationId: notification.notificationId,
2000
+ notificationType: notification.notificationType || details.payload?.notificationType,
2001
+ worldId: details.worldId,
2002
+ conversationKey: details.conversationKey,
2003
+ targetAgentId: details.targetAgentId,
2004
+ sessionKey: localSessionKey || details.sessionKey,
2005
+ relaySessionKey: details.sessionKey,
2006
+ };
2007
+ return buildClaworldRuntimeMaintenanceEvent({
2008
+ id: details.eventId ? `runtime:${details.eventType}:${details.eventId}` : null,
2009
+ timestamp: details.createdAt || notification.createdAt || null,
2010
+ kind: details.eventName || details.eventType || 'runtime_event',
2011
+ eventType: details.eventType || 'runtime_event',
2012
+ scope: 'management',
2013
+ summary: notification.title || notification.body || `${details.eventType || 'runtime'} event received.`,
2014
+ excerpt: buildManagementRuntimeEventBody(details),
2015
+ refs,
2016
+ relations: {
2017
+ inboxItemId: refs.inboxItemId,
2018
+ notificationId: refs.notificationId,
2019
+ notificationType: refs.notificationType,
2020
+ worldId: details.worldId,
2021
+ conversationKey: details.conversationKey,
2022
+ targetAgentId: details.targetAgentId,
2023
+ localAgentId,
2024
+ localSessionKey,
2025
+ relaySessionKey: details.sessionKey,
2026
+ sessionKey: localSessionKey || details.sessionKey,
2027
+ sessionId: sessionArtifacts.sessionId,
2028
+ sessionFile: sessionArtifacts.sessionFile,
2029
+ sessionStorePath: sessionArtifacts.sessionStorePath,
2030
+ transcriptPath: sessionArtifacts.transcriptPath,
2031
+ },
2032
+ artifacts: {
2033
+ workspaceRoot,
2034
+ ...sessionArtifacts,
2035
+ },
2036
+ });
2037
+ }
2038
+
1810
2039
  function createDeliveryReplyDispatcher({
1811
2040
  runtime,
1812
2041
  currentCfg,
@@ -2298,12 +2527,15 @@ async function maybeBridgeRuntimeDelivery({
2298
2527
  UntrustedContext,
2299
2528
  });
2300
2529
 
2530
+ let sessionStorePath = null;
2531
+ let sessionRecord = null;
2532
+ let sessionArtifacts = {};
2301
2533
  if (runtime?.channel?.session?.recordInboundSession && runtime?.channel?.session?.resolveStorePath && localAgentId) {
2302
- const storePath = runtime.channel.session.resolveStorePath(currentCfg.session?.store, {
2534
+ sessionStorePath = runtime.channel.session.resolveStorePath(currentCfg.session?.store, {
2303
2535
  agentId: localAgentId,
2304
2536
  });
2305
- await runtime.channel.session.recordInboundSession({
2306
- storePath,
2537
+ sessionRecord = await runtime.channel.session.recordInboundSession({
2538
+ storePath: sessionStorePath,
2307
2539
  sessionKey: inboundCtx.SessionKey || sessionKey,
2308
2540
  ctx: inboundCtx,
2309
2541
  onRecordError: (error) => {
@@ -2316,6 +2548,46 @@ async function maybeBridgeRuntimeDelivery({
2316
2548
  });
2317
2549
  },
2318
2550
  });
2551
+ sessionArtifacts = resolveSessionRecordArtifacts(sessionRecord, sessionStorePath);
2552
+ }
2553
+
2554
+ const workspaceRoot = resolveOpenClawWorkspaceRoot({
2555
+ sources: [
2556
+ { agentId: localAgentId, localAgentId },
2557
+ currentCfg,
2558
+ runtimeConfig,
2559
+ ],
2560
+ config: currentCfg,
2561
+ agentId: localAgentId,
2562
+ });
2563
+ let journalResult = null;
2564
+ if (workspaceRoot) {
2565
+ try {
2566
+ journalResult = await appendClaworldJournalEvent(
2567
+ workspaceRoot,
2568
+ buildDeliveryRuntimeMaintenanceEvent({
2569
+ delivery,
2570
+ metadata,
2571
+ payload,
2572
+ localSessionKey,
2573
+ localAgentId,
2574
+ sessionArtifacts,
2575
+ workspaceRoot,
2576
+ }),
2577
+ {
2578
+ trigger: 'delivery',
2579
+ rule: 'runtime_delivery_joined_session',
2580
+ },
2581
+ );
2582
+ } catch (error) {
2583
+ logger.warn?.(`[claworld:${runtimeAccountId}] delivery journal append failed`, {
2584
+ deliveryId,
2585
+ sessionKey,
2586
+ localSessionKey,
2587
+ localAgentId,
2588
+ error: error?.message || String(error),
2589
+ });
2590
+ }
2319
2591
  }
2320
2592
 
2321
2593
  logger.info?.(`[claworld:${runtimeAccountId}] routing delivery into runtime session`, {
@@ -2323,12 +2595,15 @@ async function maybeBridgeRuntimeDelivery({
2323
2595
  sessionKey,
2324
2596
  localSessionKey,
2325
2597
  localAgentId,
2598
+ sessionId: sessionArtifacts.sessionId || null,
2599
+ sessionFile: sessionArtifacts.sessionFile || null,
2326
2600
  remoteIdentity,
2327
2601
  routeStatus: routed?.status || null,
2328
2602
  bodyPreview: String(Body || '').slice(0, 240),
2329
2603
  rawBodyPreview: String(RawBody || '').slice(0, 240),
2330
2604
  allowReply: metadata.allowReply !== false,
2331
2605
  commandAuthorized,
2606
+ journal: journalResult?.ok === true,
2332
2607
  });
2333
2608
 
2334
2609
  try {
@@ -2437,6 +2712,206 @@ async function maybeBridgeRuntimeDelivery({
2437
2712
  };
2438
2713
  }
2439
2714
 
2715
+ async function maybeBridgeRuntimeManagementEvent({
2716
+ runtimeConfig,
2717
+ runtimeAccountId,
2718
+ event,
2719
+ logger,
2720
+ runtime,
2721
+ cfg,
2722
+ inbound,
2723
+ }) {
2724
+ const details = resolveRuntimeManagementEnvelope(event);
2725
+ const route = event?.route || inbound?.routeInboundEvent?.(details.envelope, {
2726
+ sessionTarget: runtimeConfig.routing?.sessionTarget,
2727
+ fallbackTarget: runtimeConfig.routing?.fallbackTarget,
2728
+ }) || null;
2729
+ const sessionKey = resolveNormalizedText(details.sessionKey, resolveNormalizedText(route?.sessionKey, null));
2730
+ const eventId = resolveNormalizedText(details.eventId, `${details.eventType || 'runtime_event'}:${Date.now()}`);
2731
+
2732
+ if (
2733
+ !runtime?.channel?.reply?.finalizeInboundContext
2734
+ || !runtime?.channel?.reply?.dispatchReplyFromConfig
2735
+ || !runtime?.channel?.reply?.createReplyDispatcherWithTyping
2736
+ ) {
2737
+ logger.warn?.(`[claworld:${runtimeAccountId}] skipping management event bridge: missing runtime bridge hooks`, {
2738
+ eventType: details.eventType,
2739
+ eventName: details.eventName,
2740
+ sessionKey,
2741
+ });
2742
+ return { skipped: true, reason: 'missing_runtime_bridge_hooks' };
2743
+ }
2744
+ if (!details.eventType || !sessionKey || route?.status === 'invalid') {
2745
+ logger.warn?.(`[claworld:${runtimeAccountId}] skipping management event bridge: missing runtime event payload`, {
2746
+ eventType: details.eventType,
2747
+ eventName: details.eventName,
2748
+ sessionKey,
2749
+ routeStatus: route?.status || null,
2750
+ });
2751
+ return { skipped: true, reason: 'missing_management_event_payload' };
2752
+ }
2753
+
2754
+ const loadedCfg = await runtime.config?.loadConfig?.() || {};
2755
+ const currentCfg = {
2756
+ ...(loadedCfg && typeof loadedCfg === 'object' && !Array.isArray(loadedCfg) ? loadedCfg : {}),
2757
+ ...(cfg && typeof cfg === 'object' && !Array.isArray(cfg) ? cfg : {}),
2758
+ agents: cfg?.agents || loadedCfg?.agents,
2759
+ bindings: cfg?.bindings || loadedCfg?.bindings,
2760
+ channels: cfg?.channels || loadedCfg?.channels,
2761
+ session: cfg?.session || loadedCfg?.session,
2762
+ };
2763
+ const localAgentId = resolveBoundLocalAgentId({
2764
+ cfg: currentCfg,
2765
+ runtimeConfig,
2766
+ });
2767
+ const localSessionKey = buildAgentScopedLocalSessionKey({
2768
+ sessionKey,
2769
+ localAgentId,
2770
+ });
2771
+ const body = buildManagementRuntimeEventBody(details, { localSessionKey });
2772
+ const timestamp = Date.parse(details.createdAt || '');
2773
+ const inboundTimestamp = Number.isFinite(timestamp) ? timestamp : Date.now();
2774
+ const localIdentity = normalizeClaworldText(runtimeConfig.relay?.agentId, runtimeConfig.accountId);
2775
+ const inboundCtx = runtime.channel.reply.finalizeInboundContext({
2776
+ Body: body,
2777
+ RawBody: body,
2778
+ CommandBody: body,
2779
+ BodyForAgent: body,
2780
+ BodyForCommands: body,
2781
+ From: 'claworld:management',
2782
+ To: `claworld:${localIdentity}`,
2783
+ SessionKey: localSessionKey || sessionKey,
2784
+ RelaySessionKey: sessionKey,
2785
+ AccountId: runtimeConfig.accountId,
2786
+ OriginatingChannel: 'claworld',
2787
+ OriginatingFrom: 'management',
2788
+ OriginatingTo: localIdentity,
2789
+ ChatType: 'management',
2790
+ SessionType: 'management',
2791
+ sessionType: 'management',
2792
+ sessionKind: 'management',
2793
+ SenderName: 'Claworld Management',
2794
+ SenderId: 'claworld-management',
2795
+ MessageId: eventId,
2796
+ Provider: 'claworld',
2797
+ Surface: 'claworld',
2798
+ ConversationLabel: 'Claworld Management',
2799
+ Timestamp: inboundTimestamp,
2800
+ MessageSid: eventId,
2801
+ WasMentioned: false,
2802
+ CommandAuthorized: false,
2803
+ RelayEventId: eventId,
2804
+ RelayEventType: details.eventType,
2805
+ RelayEventName: details.eventName,
2806
+ RelayTargetAgentId: details.targetAgentId,
2807
+ });
2808
+
2809
+ let sessionStorePath = null;
2810
+ let sessionRecord = null;
2811
+ let sessionArtifacts = {};
2812
+ if (runtime?.channel?.session?.recordInboundSession && runtime?.channel?.session?.resolveStorePath && localAgentId) {
2813
+ sessionStorePath = runtime.channel.session.resolveStorePath(currentCfg.session?.store, {
2814
+ agentId: localAgentId,
2815
+ });
2816
+ sessionRecord = await runtime.channel.session.recordInboundSession({
2817
+ storePath: sessionStorePath,
2818
+ sessionKey: inboundCtx.SessionKey || sessionKey,
2819
+ ctx: inboundCtx,
2820
+ onRecordError: (error) => {
2821
+ logger.error?.(`[claworld:${runtimeAccountId}] failed to record management inbound session`, {
2822
+ eventType: details.eventType,
2823
+ eventName: details.eventName,
2824
+ sessionKey,
2825
+ localSessionKey,
2826
+ localAgentId,
2827
+ error: error?.message || String(error),
2828
+ });
2829
+ },
2830
+ });
2831
+ sessionArtifacts = resolveSessionRecordArtifacts(sessionRecord, sessionStorePath);
2832
+ }
2833
+
2834
+ const workspaceRoot = resolveOpenClawWorkspaceRoot({
2835
+ sources: [
2836
+ { agentId: localAgentId, localAgentId },
2837
+ currentCfg,
2838
+ runtimeConfig,
2839
+ ],
2840
+ config: currentCfg,
2841
+ agentId: localAgentId,
2842
+ });
2843
+ let journalResult = null;
2844
+ if (workspaceRoot) {
2845
+ try {
2846
+ journalResult = await appendClaworldJournalEvent(
2847
+ workspaceRoot,
2848
+ buildManagementRuntimeMaintenanceEvent(details, {
2849
+ localSessionKey,
2850
+ localAgentId,
2851
+ sessionArtifacts,
2852
+ workspaceRoot,
2853
+ }),
2854
+ );
2855
+ } catch (error) {
2856
+ logger.warn?.(`[claworld:${runtimeAccountId}] management event journal append failed`, {
2857
+ eventType: details.eventType,
2858
+ eventName: details.eventName,
2859
+ sessionKey,
2860
+ error: error?.message || String(error),
2861
+ });
2862
+ }
2863
+ }
2864
+
2865
+ logger.info?.(`[claworld:${runtimeAccountId}] routing management event into runtime session`, {
2866
+ eventType: details.eventType,
2867
+ eventName: details.eventName,
2868
+ eventId,
2869
+ sessionKey,
2870
+ localSessionKey,
2871
+ localAgentId,
2872
+ sessionId: sessionArtifacts.sessionId || null,
2873
+ sessionFile: sessionArtifacts.sessionFile || null,
2874
+ routeStatus: route?.status || null,
2875
+ journal: journalResult?.ok === true,
2876
+ });
2877
+
2878
+ const {
2879
+ dispatchResult,
2880
+ runtimeOutputSummary,
2881
+ } = await runDeliveryReplyDispatch({
2882
+ runtime,
2883
+ currentCfg,
2884
+ relayClient: null,
2885
+ deliveryId: eventId,
2886
+ sessionKey,
2887
+ localAgentId,
2888
+ allowReply: false,
2889
+ logger,
2890
+ runtimeAccountId,
2891
+ inboundCtx,
2892
+ });
2893
+
2894
+ logger.info?.(`[claworld:${runtimeAccountId}] management event bridge completed`, {
2895
+ eventType: details.eventType,
2896
+ eventName: details.eventName,
2897
+ eventId,
2898
+ sessionKey,
2899
+ localSessionKey,
2900
+ queuedFinal: Boolean(dispatchResult?.queuedFinal),
2901
+ routeStatus: route?.status || null,
2902
+ runtimeOutputSummary,
2903
+ });
2904
+
2905
+ return {
2906
+ skipped: false,
2907
+ ok: true,
2908
+ queuedFinal: Boolean(dispatchResult?.queuedFinal),
2909
+ sessionKey,
2910
+ localSessionKey,
2911
+ routeStatus: route?.status || null,
2912
+ };
2913
+ }
2914
+
2440
2915
  export function createClaworldChannelPlugin({
2441
2916
  logger = console,
2442
2917
  relayClientFactory = createClaworldRelayClient,
@@ -2871,6 +3346,7 @@ export function createClaworldChannelPlugin({
2871
3346
  eventType: event?.eventType || null,
2872
3347
  target: event?.route?.target || null,
2873
3348
  deliveryId: event?.delivery?.deliveryId || null,
3349
+ eventId: event?.delivery?.eventId || null,
2874
3350
  sessionKey: event?.delivery?.sessionKey || null,
2875
3351
  });
2876
3352
 
@@ -2890,6 +3366,21 @@ export function createClaworldChannelPlugin({
2890
3366
  error: error?.message || String(error),
2891
3367
  });
2892
3368
  });
3369
+ } else if (event?.route?.sessionKind === 'management' || event?.delivery?.sessionKey?.startsWith?.('management:')) {
3370
+ const runtimeContext = accountRuntimeContexts.get(accountKey) || {};
3371
+ maybeBridgeRuntimeManagementEvent({
3372
+ runtimeConfig,
3373
+ runtimeAccountId,
3374
+ event,
3375
+ logger,
3376
+ runtime: runtimeContext.runtime,
3377
+ cfg: runtimeContext.cfg,
3378
+ inbound,
3379
+ }).catch((error) => {
3380
+ logger.error?.(`[claworld:${runtimeAccountId}] management event bridge exception`, {
3381
+ error: error?.message || String(error),
3382
+ });
3383
+ });
2893
3384
  }
2894
3385
  });
2895
3386
 
@@ -653,19 +653,7 @@ export function projectToolAccountViewResponse({
653
653
  identityPayload = null,
654
654
  } = {}) {
655
655
  const identityReady = identityPayload?.ready === true;
656
- const activationReady = pairingPayload?.status === 'paired';
657
- const bindingReady = typeof pairingPayload?.bindingReady === 'boolean'
658
- ? pairingPayload.bindingReady
659
- : activationReady;
660
- const bindingStatus = normalizeText(
661
- pairingPayload?.bindingStatus,
662
- activationReady
663
- ? (bindingReady ? 'bound' : 'identity_unresolved')
664
- : 'unactivated',
665
- );
666
- const ready = activationReady && identityReady;
667
- const relayResolved = pairingPayload?.relayAgent?.resolved ?? null;
668
- const relayOnline = pairingPayload?.relayAgent?.online ?? null;
656
+ const ready = pairingPayload?.status === 'paired' && identityReady;
669
657
  const resolvedShareCard = identityPayload && Object.prototype.hasOwnProperty.call(identityPayload, 'shareCard')
670
658
  ? projectToolShareCard(identityPayload.shareCard)
671
659
  : undefined;
@@ -680,15 +668,7 @@ export function projectToolAccountViewResponse({
680
668
  reason: normalizeText(pairingPayload?.reason, null),
681
669
  bindingSource: normalizeText(pairingPayload?.bindingSource, null),
682
670
  activation: {
683
- status: activationReady ? 'ready' : 'pending',
684
- },
685
- diagnostics: {
686
- toolReachable: true,
687
- bindingReady,
688
- bindingStatus,
689
- publicIdentityReady: identityReady,
690
- relayPresenceResolved: relayResolved,
691
- relayOnline,
671
+ status: pairingPayload?.status === 'paired' ? 'ready' : 'pending',
692
672
  },
693
673
  relay: {
694
674
  agentId: normalizeText(
@@ -698,9 +678,9 @@ export function projectToolAccountViewResponse({
698
678
  displayName: normalizeText(pairingPayload?.relayAgent?.displayName, null),
699
679
  discoverable: pairingPayload?.relayAgent?.discoverable ?? null,
700
680
  contactable: pairingPayload?.relayAgent?.contactable ?? null,
701
- online: relayOnline,
702
- resolved: relayResolved,
703
- bindingStatus,
681
+ online: pairingPayload?.relayAgent?.online ?? null,
682
+ resolved: pairingPayload?.relayAgent?.resolved ?? null,
683
+ bindingStatus: pairingPayload?.status === 'paired' ? 'bound' : 'unactivated',
704
684
  },
705
685
  profile: projectToolAccountProfile(identityPayload),
706
686
  ...projectToolAccountIdentityFields(identityPayload),