@xfxstudio/claworld 2026.5.10-testing.1 → 2026.5.11-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.
@@ -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.5.10-testing.1",
11
+ "version": "2026.5.11-testing.1",
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.5.10-testing.1",
3
+ "version": "2026.5.11-testing.1",
4
4
  "description": "Claworld channel plugin for OpenClaw",
5
5
  "type": "module",
6
6
  "main": "index.js",
@@ -1,4 +1,5 @@
1
1
  import { randomUUID } from 'node:crypto';
2
+ import path from 'node:path';
2
3
 
3
4
  import {
4
5
  applyRuntimeIdentity,
@@ -32,6 +33,10 @@ import { createInboundSessionRouter } from '../runtime/inbound-session-router.js
32
33
  import { createOutboundSessionBridge } from '../runtime/outbound-session-bridge.js';
33
34
  import { createCanonicalResultBuilder } from '../runtime/canonical-result-builder.js';
34
35
  import { createDemoSessionBootstrap } from '../runtime/demo-session-bootstrap.js';
36
+ import {
37
+ appendClaworldJournalEvent,
38
+ buildClaworldRuntimeMaintenanceEvent,
39
+ } from '../runtime/working-memory.js';
35
40
  import {
36
41
  broadcastModeratedWorld,
37
42
  createModeratedWorld,
@@ -62,6 +67,7 @@ import {
62
67
  resolveWorldSelectionFlow,
63
68
  } from '../runtime/product-shell-helper.js';
64
69
  import { extractBackendErrorContext } from '../runtime/backend-error-context.js';
70
+ import { resolveOpenClawWorkspaceRoot } from '../runtime/workspace-resolver.js';
65
71
  import { getClaworldRuntime } from './runtime.js';
66
72
  import {
67
73
  CLAWORLD_PLUGIN_CURRENT_VERSION,
@@ -1841,6 +1847,297 @@ function buildDeliveryInboundEnvelope({
1841
1847
  };
1842
1848
  }
1843
1849
 
1850
+ function normalizeSessionStoreKey(value) {
1851
+ return resolveNormalizedText(value, '').toLowerCase();
1852
+ }
1853
+
1854
+ function resolveSessionStoreEntry(store = null, sessionKey = null) {
1855
+ if (!store || typeof store !== 'object' || Array.isArray(store)) return null;
1856
+ const normalizedSessionKey = resolveNormalizedText(sessionKey, null);
1857
+ if (!normalizedSessionKey) return null;
1858
+ if (store[normalizedSessionKey] && typeof store[normalizedSessionKey] === 'object') {
1859
+ return store[normalizedSessionKey];
1860
+ }
1861
+ const lowerSessionKey = normalizeSessionStoreKey(normalizedSessionKey);
1862
+ if (store[lowerSessionKey] && typeof store[lowerSessionKey] === 'object') {
1863
+ return store[lowerSessionKey];
1864
+ }
1865
+ const match = Object.entries(store).find(([key, value]) => (
1866
+ normalizeSessionStoreKey(key) === lowerSessionKey
1867
+ && value
1868
+ && typeof value === 'object'
1869
+ && !Array.isArray(value)
1870
+ ));
1871
+ return match ? match[1] : null;
1872
+ }
1873
+
1874
+ function readRuntimeSessionStoreEntry({ runtime = null, sessionStorePath = null, sessionKey = null } = {}) {
1875
+ if (!runtime?.agent?.session?.loadSessionStore || !sessionStorePath || !sessionKey) return null;
1876
+ try {
1877
+ return resolveSessionStoreEntry(
1878
+ runtime.agent.session.loadSessionStore(sessionStorePath),
1879
+ sessionKey,
1880
+ );
1881
+ } catch {
1882
+ return null;
1883
+ }
1884
+ }
1885
+
1886
+ function resolveSessionFilePathFromRuntime({
1887
+ runtime = null,
1888
+ sessionId = null,
1889
+ record = {},
1890
+ sessionStorePath = null,
1891
+ localAgentId = null,
1892
+ } = {}) {
1893
+ const normalizedSessionId = resolveNormalizedText(sessionId, null);
1894
+ if (!normalizedSessionId) return null;
1895
+
1896
+ const sessionsDir = sessionStorePath
1897
+ ? path.dirname(path.resolve(sessionStorePath))
1898
+ : null;
1899
+ if (typeof runtime?.agent?.session?.resolveSessionFilePath === 'function') {
1900
+ try {
1901
+ const resolved = runtime.agent.session.resolveSessionFilePath(
1902
+ normalizedSessionId,
1903
+ record,
1904
+ {
1905
+ ...(sessionsDir ? { sessionsDir } : {}),
1906
+ ...(localAgentId ? { agentId: localAgentId } : {}),
1907
+ },
1908
+ );
1909
+ const normalized = resolveNormalizedText(resolved, null);
1910
+ if (normalized) return normalized;
1911
+ } catch {
1912
+ // Fall through to the local derivation below.
1913
+ }
1914
+ }
1915
+
1916
+ const candidate = resolveNormalizedText(record?.sessionFile, null);
1917
+ if (candidate) {
1918
+ if (path.isAbsolute(candidate) || !sessionsDir) return candidate;
1919
+ return path.resolve(sessionsDir, candidate);
1920
+ }
1921
+
1922
+ if (sessionsDir) {
1923
+ return path.join(sessionsDir, `${normalizedSessionId}.jsonl`);
1924
+ }
1925
+ return null;
1926
+ }
1927
+
1928
+ function resolveSessionRecordArtifacts(record = null, fallbackStorePath = null, options = {}) {
1929
+ const normalizedRecord = record && typeof record === 'object' && !Array.isArray(record) ? record : {};
1930
+ const nestedSession = normalizedRecord.session && typeof normalizedRecord.session === 'object' && !Array.isArray(normalizedRecord.session)
1931
+ ? normalizedRecord.session
1932
+ : {};
1933
+ const sessionId = resolveNormalizedText(
1934
+ normalizedRecord.sessionId,
1935
+ resolveNormalizedText(nestedSession.sessionId, resolveNormalizedText(normalizedRecord.id, null)),
1936
+ );
1937
+ const sessionStorePath = resolveNormalizedText(
1938
+ normalizedRecord.storePath,
1939
+ resolveNormalizedText(normalizedRecord.sessionStorePath, fallbackStorePath),
1940
+ );
1941
+ const directSessionFile = resolveNormalizedText(
1942
+ normalizedRecord.sessionFile,
1943
+ resolveNormalizedText(
1944
+ normalizedRecord.sessionPath,
1945
+ resolveNormalizedText(
1946
+ normalizedRecord.filePath,
1947
+ resolveNormalizedText(normalizedRecord.path, resolveNormalizedText(nestedSession.filePath, null)),
1948
+ ),
1949
+ ),
1950
+ );
1951
+ const sessionFile = directSessionFile || resolveSessionFilePathFromRuntime({
1952
+ runtime: options.runtime,
1953
+ sessionId,
1954
+ record: normalizedRecord,
1955
+ sessionStorePath,
1956
+ localAgentId: options.localAgentId,
1957
+ });
1958
+ const transcriptPath = resolveNormalizedText(
1959
+ normalizedRecord.transcriptPath,
1960
+ resolveNormalizedText(nestedSession.transcriptPath, sessionFile),
1961
+ );
1962
+ return {
1963
+ sessionId,
1964
+ sessionFile,
1965
+ sessionStorePath,
1966
+ transcriptPath,
1967
+ };
1968
+ }
1969
+
1970
+ async function recordRuntimeInboundSessionArtifacts({
1971
+ runtime = null,
1972
+ currentCfg = {},
1973
+ localAgentId = null,
1974
+ sessionKey = null,
1975
+ ctx = {},
1976
+ logger = console,
1977
+ runtimeAccountId = null,
1978
+ logLabel = 'inbound',
1979
+ logContext = {},
1980
+ } = {}) {
1981
+ let sessionStorePath = null;
1982
+ let sessionRecord = null;
1983
+ const sessionApi = runtime?.channel?.session || {};
1984
+ const localSessionKey = resolveNormalizedText(ctx?.SessionKey, sessionKey);
1985
+
1986
+ if (sessionApi.resolveStorePath && localAgentId) {
1987
+ sessionStorePath = sessionApi.resolveStorePath(currentCfg.session?.store, {
1988
+ agentId: localAgentId,
1989
+ });
1990
+ const onRecordError = (error) => {
1991
+ logger.error?.(`[claworld:${runtimeAccountId}] failed to record ${logLabel} inbound session`, {
1992
+ ...logContext,
1993
+ sessionKey,
1994
+ localSessionKey,
1995
+ localAgentId,
1996
+ error: error?.message || String(error),
1997
+ });
1998
+ };
1999
+ try {
2000
+ if (typeof sessionApi.recordSessionMetaFromInbound === 'function') {
2001
+ sessionRecord = await sessionApi.recordSessionMetaFromInbound({
2002
+ storePath: sessionStorePath,
2003
+ sessionKey: localSessionKey,
2004
+ ctx,
2005
+ });
2006
+ } else if (typeof sessionApi.recordInboundSession === 'function') {
2007
+ sessionRecord = await sessionApi.recordInboundSession({
2008
+ storePath: sessionStorePath,
2009
+ sessionKey: localSessionKey,
2010
+ ctx,
2011
+ onRecordError,
2012
+ });
2013
+ }
2014
+ } catch (error) {
2015
+ onRecordError(error);
2016
+ }
2017
+ if (!sessionRecord) {
2018
+ sessionRecord = readRuntimeSessionStoreEntry({
2019
+ runtime,
2020
+ sessionStorePath,
2021
+ sessionKey: localSessionKey,
2022
+ });
2023
+ }
2024
+ }
2025
+
2026
+ return {
2027
+ sessionStorePath,
2028
+ sessionRecord,
2029
+ sessionArtifacts: resolveSessionRecordArtifacts(sessionRecord, sessionStorePath, {
2030
+ runtime,
2031
+ localAgentId,
2032
+ }),
2033
+ };
2034
+ }
2035
+
2036
+ function buildInboundRuntimeMaintenanceEvent({
2037
+ delivery = {},
2038
+ metadata = {},
2039
+ payload = {},
2040
+ messageId = null,
2041
+ eventType = 'delivery',
2042
+ sessionKind = null,
2043
+ localSessionKey = null,
2044
+ localAgentId = null,
2045
+ sessionArtifacts = {},
2046
+ workspaceRoot = null,
2047
+ } = {}) {
2048
+ const normalizedEventType = resolveNormalizedText(eventType, 'delivery');
2049
+ const isRelayDelivery = normalizedEventType === 'delivery';
2050
+ const sessionKey = resolveNormalizedText(delivery.sessionKey, null);
2051
+ const requestId = resolveNormalizedText(
2052
+ metadata.kickoffRequestId,
2053
+ resolveNormalizedText(metadata.requestId, resolveNormalizedText(metadata.chatRequestId, null)),
2054
+ );
2055
+ const worldId = resolveNormalizedText(
2056
+ metadata.worldId,
2057
+ resolveNormalizedText(delivery.worldId, resolveNormalizedText(payload.worldId, null)),
2058
+ );
2059
+ const conversationKey = resolveNormalizedText(
2060
+ metadata.conversationKey,
2061
+ resolveNormalizedText(delivery.conversationKey, resolveNormalizedText(payload.conversationKey, null)),
2062
+ );
2063
+ const fromAgentId = resolveNormalizedText(metadata.fromAgentId, null);
2064
+ const targetAgentId = resolveNormalizedText(
2065
+ delivery.targetAgentId,
2066
+ resolveNormalizedText(payload.targetAgentId, resolveNormalizedText(metadata.targetAgentId, null)),
2067
+ );
2068
+ const notificationId = resolveNormalizedText(
2069
+ metadata.notificationId,
2070
+ resolveNormalizedText(payload.notificationId, null),
2071
+ );
2072
+ const inboxItemId = resolveNormalizedText(
2073
+ metadata.inboxItemId,
2074
+ resolveNormalizedText(payload.inboxItemId, null),
2075
+ );
2076
+ const scope = sessionKind === 'management' ? 'management' : 'conversation';
2077
+ const summary = [
2078
+ isRelayDelivery
2079
+ ? 'Inbound Claworld delivery joined local session'
2080
+ : 'Inbound Claworld runtime input joined local session',
2081
+ requestId ? `for request ${requestId}` : null,
2082
+ fromAgentId ? `from ${fromAgentId}` : null,
2083
+ ].filter(Boolean).join(' ');
2084
+ return buildClaworldRuntimeMaintenanceEvent({
2085
+ id: messageId ? `runtime:${normalizedEventType}:${messageId}` : null,
2086
+ timestamp: delivery.createdAt || metadata.createdAt || payload.createdAt || null,
2087
+ kind: isRelayDelivery
2088
+ ? (metadata.deliveryType ? `delivery.${metadata.deliveryType}` : 'delivery')
2089
+ : 'runtime_event',
2090
+ eventType: normalizedEventType,
2091
+ scope,
2092
+ summary,
2093
+ excerpt: isRelayDelivery
2094
+ ? (
2095
+ payload.contextText
2096
+ ? 'Inbound delivery included contextText; raw dialogue is kept in the OpenClaw session transcript.'
2097
+ : 'Inbound delivery routed into an OpenClaw session after backend session resolution.'
2098
+ )
2099
+ : 'Inbound runtime input routed into an OpenClaw session after backend session resolution.',
2100
+ refs: {
2101
+ deliveryId: isRelayDelivery ? messageId : null,
2102
+ eventId: messageId,
2103
+ requestId,
2104
+ chatRequestId: requestId,
2105
+ worldId,
2106
+ conversationKey,
2107
+ fromAgentId,
2108
+ targetAgentId,
2109
+ notificationId,
2110
+ inboxItemId,
2111
+ sessionKey: localSessionKey || sessionKey,
2112
+ relaySessionKey: sessionKey,
2113
+ },
2114
+ relations: {
2115
+ deliveryId: isRelayDelivery ? messageId : null,
2116
+ eventId: messageId,
2117
+ requestId,
2118
+ chatRequestId: requestId,
2119
+ worldId,
2120
+ conversationKey,
2121
+ fromAgentId,
2122
+ targetAgentId,
2123
+ notificationId,
2124
+ inboxItemId,
2125
+ localAgentId,
2126
+ localSessionKey,
2127
+ relaySessionKey: sessionKey,
2128
+ sessionKey: localSessionKey || sessionKey,
2129
+ sessionId: sessionArtifacts.sessionId,
2130
+ sessionFile: sessionArtifacts.sessionFile,
2131
+ sessionStorePath: sessionArtifacts.sessionStorePath,
2132
+ transcriptPath: sessionArtifacts.transcriptPath,
2133
+ },
2134
+ artifacts: {
2135
+ workspaceRoot,
2136
+ ...sessionArtifacts,
2137
+ },
2138
+ });
2139
+ }
2140
+
1844
2141
  function createDeliveryReplyDispatcher({
1845
2142
  runtime,
1846
2143
  currentCfg,
@@ -2232,18 +2529,14 @@ async function maybeBridgeRuntimeInboundEvent({
2232
2529
  const contextText = resolveNormalizedText(payload.contextText, null);
2233
2530
  const incomingText = resolveNormalizedText(
2234
2531
  payload.commandText,
2235
- contextText ? null : resolveNormalizedText(payload.text, resolveNormalizedText(payload.body, null)),
2532
+ contextText
2533
+ ? null
2534
+ : resolveNormalizedText(payload.text, resolveNormalizedText(payload.body, null)),
2236
2535
  );
2237
2536
  const commandText = resolveNormalizedText(payload.commandText, incomingText);
2238
2537
  const fromAgentId = resolveNormalizedText(metadata.fromAgentId, null);
2239
- const routeSessionKind = resolveNormalizedText(
2240
- event?.route?.sessionKind,
2241
- resolveNormalizedText(delivery.sessionKind, resolveNormalizedText(payload.sessionKind, null)),
2242
- );
2243
2538
  const isRelayDelivery = eventType === 'delivery';
2244
2539
  const allowReply = metadata.allowReply === true || (isRelayDelivery && metadata.allowReply !== false);
2245
- const remoteIdentity = fromAgentId
2246
- || resolveNormalizedText(metadata.source, routeSessionKind === 'management' ? 'claworld-management' : 'unknown-peer');
2247
2540
 
2248
2541
  if (
2249
2542
  !runtime?.channel?.reply?.finalizeInboundContext
@@ -2290,6 +2583,12 @@ async function maybeBridgeRuntimeInboundEvent({
2290
2583
  sessionTarget: runtimeConfig.routing?.sessionTarget,
2291
2584
  fallbackTarget: runtimeConfig.routing?.fallbackTarget,
2292
2585
  }) || null;
2586
+ const routeSessionKind = resolveNormalizedText(
2587
+ event?.route?.sessionKind,
2588
+ resolveNormalizedText(routed?.sessionKind, null),
2589
+ );
2590
+ const remoteIdentity = fromAgentId
2591
+ || resolveNormalizedText(metadata.source, routeSessionKind === 'management' ? 'claworld-management' : 'unknown-peer');
2293
2592
  const worldId = resolveDeliveryWorldId(delivery);
2294
2593
  const commandAuthorized = isRelayDelivery && shouldAuthorizeBridgedCommand({
2295
2594
  runtimeConfig,
@@ -2331,6 +2630,9 @@ async function maybeBridgeRuntimeInboundEvent({
2331
2630
  OriginatingFrom: remoteIdentity,
2332
2631
  OriginatingTo: remoteIdentity,
2333
2632
  ChatType: isManagementSession ? 'management' : 'direct',
2633
+ SessionType: isManagementSession ? 'management' : 'direct',
2634
+ sessionType: isManagementSession ? 'management' : 'direct',
2635
+ sessionKind: isManagementSession ? 'management' : 'conversation',
2334
2636
  SenderName: senderName,
2335
2637
  SenderId: remoteIdentity,
2336
2638
  MessageId: deliveryId,
@@ -2346,26 +2648,21 @@ async function maybeBridgeRuntimeInboundEvent({
2346
2648
  UntrustedContext,
2347
2649
  });
2348
2650
 
2349
- if (runtime?.channel?.session?.recordInboundSession && runtime?.channel?.session?.resolveStorePath && localAgentId) {
2350
- const storePath = runtime.channel.session.resolveStorePath(currentCfg.session?.store, {
2351
- agentId: localAgentId,
2352
- });
2353
- await runtime.channel.session.recordInboundSession({
2354
- storePath,
2355
- sessionKey: inboundCtx.SessionKey || sessionKey,
2356
- ctx: inboundCtx,
2357
- onRecordError: (error) => {
2358
- logger.error?.(`[claworld:${runtimeAccountId}] failed to record inbound session`, {
2359
- eventType,
2360
- deliveryId,
2361
- sessionKey,
2362
- localSessionKey,
2363
- localAgentId,
2364
- error: error?.message || String(error),
2365
- });
2366
- },
2367
- });
2368
- }
2651
+ const {
2652
+ sessionArtifacts,
2653
+ } = await recordRuntimeInboundSessionArtifacts({
2654
+ runtime,
2655
+ currentCfg,
2656
+ localAgentId,
2657
+ sessionKey,
2658
+ ctx: inboundCtx,
2659
+ logger,
2660
+ runtimeAccountId,
2661
+ logContext: {
2662
+ eventType,
2663
+ deliveryId,
2664
+ },
2665
+ });
2369
2666
 
2370
2667
  logger.info?.(`[claworld:${runtimeAccountId}] ${isRelayDelivery ? 'routing delivery into runtime session' : 'routing inbound event into runtime session'}`, {
2371
2668
  eventType,
@@ -2467,16 +2764,54 @@ async function maybeBridgeRuntimeInboundEvent({
2467
2764
  }));
2468
2765
  }
2469
2766
 
2767
+ let journalResult = null;
2768
+ const workspaceRoot = resolveOpenClawWorkspaceRoot({
2769
+ sources: [
2770
+ { agentId: localAgentId, localAgentId },
2771
+ currentCfg,
2772
+ runtimeConfig,
2773
+ ],
2774
+ config: currentCfg,
2775
+ agentId: localAgentId,
2776
+ });
2777
+ if (workspaceRoot) {
2778
+ try {
2779
+ const maintenanceEvent = buildInboundRuntimeMaintenanceEvent({
2780
+ delivery,
2781
+ metadata,
2782
+ payload,
2783
+ messageId: deliveryId,
2784
+ eventType,
2785
+ sessionKind: routeSessionKind,
2786
+ localSessionKey,
2787
+ localAgentId,
2788
+ sessionArtifacts,
2789
+ workspaceRoot,
2790
+ });
2791
+ journalResult = await appendClaworldJournalEvent(workspaceRoot, maintenanceEvent);
2792
+ } catch (error) {
2793
+ logger.warn?.(`[claworld:${runtimeAccountId}] inbound journal append failed`, {
2794
+ eventType,
2795
+ deliveryId,
2796
+ sessionKey,
2797
+ error: error?.message || String(error),
2798
+ });
2799
+ }
2800
+ }
2801
+
2470
2802
  logger.info?.(`[claworld:${runtimeAccountId}] ${isRelayDelivery ? 'delivery bridge completed' : 'inbound bridge completed'}`, {
2471
2803
  eventType,
2472
2804
  deliveryId,
2473
2805
  sessionKey,
2474
2806
  localSessionKey,
2807
+ sessionId: sessionArtifacts.sessionId || null,
2808
+ sessionFile: sessionArtifacts.sessionFile || null,
2475
2809
  queuedFinal: Boolean(dispatchResult?.queuedFinal),
2476
2810
  replied,
2477
2811
  keptSilent,
2478
2812
  routeStatus: routed?.status || null,
2479
2813
  runtimeOutputSummary,
2814
+ journal: journalResult?.ok === true,
2480
2815
  });
2481
2816
 
2482
2817
  return {
@@ -18,6 +18,7 @@ import {
18
18
  buildClaworldToolMaintenanceEvent,
19
19
  ensureClaworldWorkingMemory,
20
20
  resolveClaworldBootstrapTarget,
21
+ updateClaworldSessionDirectory,
21
22
  } from '../runtime/working-memory.js';
22
23
  import { resolveOpenClawWorkspaceRoot } from '../runtime/workspace-resolver.js';
23
24
  import { setClaworldRuntime } from './runtime.js';
@@ -2224,13 +2225,43 @@ export function registerClaworldPluginFull(api, plugin) {
2224
2225
  });
2225
2226
 
2226
2227
  api.on('before_tool_call', async (event, ctx) => {
2227
- if (event?.toolName !== 'claworld_manage_conversations') return;
2228
+ const toolName = normalizeText(event?.toolName, null);
2229
+ if (!toolName || !toolName.startsWith('claworld_')) return;
2228
2230
  const params = event?.params && typeof event.params === 'object' && !Array.isArray(event.params)
2229
2231
  ? event.params
2230
2232
  : {};
2231
- if (normalizeTerminalConversationAction(params.action, null) !== 'request') return;
2232
2233
  const requesterSessionKey = normalizeText(ctx?.sessionKey, null);
2233
- if (!requesterSessionKey) return;
2234
+ if (
2235
+ toolName !== 'claworld_manage_conversations'
2236
+ || normalizeTerminalConversationAction(params.action, null) !== 'request'
2237
+ || !requesterSessionKey
2238
+ ) {
2239
+ return;
2240
+ }
2241
+ const logger = getHookLogger(api);
2242
+ try {
2243
+ const workspaceRoot = await resolveHookWorkspaceRoot(api, event, ctx);
2244
+ if (workspaceRoot) {
2245
+ await updateClaworldSessionDirectory(
2246
+ workspaceRoot,
2247
+ {
2248
+ timestamp: event?.timestamp || ctx?.timestamp || null,
2249
+ source: 'claworld_hook',
2250
+ eventType: 'before_tool_call',
2251
+ kind: toolName,
2252
+ toolName,
2253
+ relations: {
2254
+ localSessionKey: requesterSessionKey,
2255
+ sessionKey: requesterSessionKey,
2256
+ localAgentId: normalizeText(ctx?.agentId ?? ctx?.AgentId, null),
2257
+ },
2258
+ context: ctx || {},
2259
+ },
2260
+ );
2261
+ }
2262
+ } catch (error) {
2263
+ logger?.warn?.('[claworld:working-memory] unable to update requester session directory', error);
2264
+ }
2234
2265
  return {
2235
2266
  params: {
2236
2267
  ...params,
@@ -2252,6 +2283,7 @@ export function registerClaworldPluginFull(api, plugin) {
2252
2283
  params: event?.params || {},
2253
2284
  result: hookToolResult(event),
2254
2285
  timestamp: event?.timestamp || ctx?.timestamp || null,
2286
+ context: ctx || {},
2255
2287
  });
2256
2288
  if (!maintenanceEvent) return;
2257
2289
  await appendClaworldJournalEvent(workspaceRoot, maintenanceEvent);
@@ -95,13 +95,23 @@ export function buildInboundEnvelope(message = {}) {
95
95
  'text',
96
96
  'body',
97
97
  'notification',
98
+ 'conversationKey',
99
+ 'worldId',
98
100
  ]) {
99
101
  if (payload[key] == null && data[key] != null) payload[key] = data[key];
100
102
  }
101
103
  }
104
+ const notification = payload.notification && typeof payload.notification === 'object' && !Array.isArray(payload.notification)
105
+ ? payload.notification
106
+ : data.notification && typeof data.notification === 'object' && !Array.isArray(data.notification)
107
+ ? data.notification
108
+ : {};
102
109
  const targetAgentId = normalizeEnvelopeText(
103
110
  data.targetAgentId,
104
- normalizeEnvelopeText(payload.targetAgentId, null),
111
+ normalizeEnvelopeText(
112
+ payload.targetAgentId,
113
+ normalizeEnvelopeText(notification.targetAgentId, normalizeEnvelopeText(metadata.targetAgentId, null)),
114
+ ),
105
115
  );
106
116
  const sessionKey = normalizeEnvelopeText(
107
117
  data.sessionKey,
@@ -109,23 +119,45 @@ export function buildInboundEnvelope(message = {}) {
109
119
  payload.sessionKey,
110
120
  normalizeEnvelopeText(
111
121
  data.targetSessionKey,
112
- normalizeEnvelopeText(payload.targetSessionKey, null),
122
+ normalizeEnvelopeText(
123
+ payload.targetSessionKey,
124
+ normalizeEnvelopeText(notification.targetSessionKey, normalizeEnvelopeText(metadata.sessionKey, null)),
125
+ ),
113
126
  ),
114
127
  ),
115
128
  );
116
129
  const isDeliveryEvent = message.event === 'delivery';
117
130
  const isRoutableEvent = Boolean(eventType && sessionKey);
118
131
  if (!isDeliveryEvent && !isRoutableEvent) return null;
132
+ const deliveryId = resolveEnvelopeMessageId(data, payload);
133
+ const eventName = normalizeEnvelopeText(
134
+ data.eventName,
135
+ normalizeEnvelopeText(payload.eventName, isDeliveryEvent ? null : normalizeEnvelopeText(message.event, null)),
136
+ );
119
137
  return {
120
138
  eventType: eventType || 'delivery',
121
- deliveryId: resolveEnvelopeMessageId(data, payload),
139
+ eventName,
140
+ eventId: deliveryId,
141
+ deliveryId,
122
142
  sessionKey,
123
143
  targetAgentId,
124
- createdAt: data.createdAt || data.availableAt || null,
125
- updatedAt: data.updatedAt || null,
144
+ conversationKey: normalizeEnvelopeText(
145
+ data.conversationKey,
146
+ normalizeEnvelopeText(payload.conversationKey, normalizeEnvelopeText(notification.relatedObjects?.conversationKey, null)),
147
+ ),
148
+ worldId: normalizeEnvelopeText(
149
+ data.worldId,
150
+ normalizeEnvelopeText(payload.worldId, normalizeEnvelopeText(notification.relatedObjects?.worldId, null)),
151
+ ),
152
+ createdAt: data.createdAt || payload.createdAt || data.availableAt || payload.availableAt || notification.createdAt || null,
153
+ updatedAt: data.updatedAt || payload.updatedAt || notification.updatedAt || null,
126
154
  turnCreatedAt: data.turnCreatedAt || null,
127
155
  payload,
128
- metadata,
156
+ metadata: {
157
+ ...metadata,
158
+ relayEvent: normalizeEnvelopeText(message.event, null),
159
+ inboxItemId: normalizeEnvelopeText(data.inboxItemId, normalizeEnvelopeText(payload.inboxItemId, null)),
160
+ },
129
161
  };
130
162
  }
131
163
 
@@ -21,16 +21,17 @@ export function createRelayEventProtocol() {
21
21
  describeEvent(event = {}) {
22
22
  const payload = normalizePayload(event.payload);
23
23
  const missing = [];
24
- if (normalizeText(event.eventType, null) !== DELIVERY_EVENT_TYPE) {
24
+ const eventType = normalizeText(event.eventType, null);
25
+ if (eventType !== DELIVERY_EVENT_TYPE) {
25
26
  missing.push('eventType');
26
27
  }
27
- if (!normalizeText(event.deliveryId, null)) {
28
+ if (eventType === DELIVERY_EVENT_TYPE && !normalizeText(event.deliveryId, null)) {
28
29
  missing.push('deliveryId');
29
30
  }
30
31
  if (!normalizeText(event.sessionKey, null)) {
31
32
  missing.push('sessionKey');
32
33
  }
33
- if (!normalizeText(payload.text, null)) {
34
+ if (eventType === DELIVERY_EVENT_TYPE && !normalizeText(payload.text, null)) {
34
35
  missing.push('payload.text');
35
36
  }
36
37
  return {
@@ -22,7 +22,6 @@ export function createInboundSessionRouter() {
22
22
  const target = resolveRuntimeSessionTarget(event, options);
23
23
  const sessionKey = target.sessionKey;
24
24
  return {
25
- action: target.sessionKind === 'management' ? 'route_management_input' : 'route_delivery',
26
25
  target: target.target,
27
26
  fallbackTarget: normalizeText(options.fallbackTarget, 'mainagent'),
28
27
  sessionKind: target.sessionKind,