@xfxstudio/claworld 2026.4.30-testing.2 → 2026.5.3-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.
@@ -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,
@@ -137,6 +138,35 @@ function resolveNormalizedText(value, fallback = null) {
137
138
  return normalizeClaworldText(value, fallback);
138
139
  }
139
140
 
141
+ function resolveInboundMessageId({ delivery = {}, payload = {}, metadata = {} } = {}) {
142
+ const notification = payload.notification && typeof payload.notification === 'object' && !Array.isArray(payload.notification)
143
+ ? payload.notification
144
+ : delivery.notification && typeof delivery.notification === 'object' && !Array.isArray(delivery.notification)
145
+ ? delivery.notification
146
+ : {};
147
+ const candidates = [
148
+ delivery.deliveryId,
149
+ delivery.inboxItemId,
150
+ delivery.messageId,
151
+ delivery.eventId,
152
+ delivery.notificationId,
153
+ payload.deliveryId,
154
+ payload.inboxItemId,
155
+ payload.messageId,
156
+ payload.eventId,
157
+ payload.notificationId,
158
+ metadata.messageId,
159
+ metadata.eventId,
160
+ metadata.notificationId,
161
+ notification.notificationId,
162
+ ];
163
+ for (const candidate of candidates) {
164
+ const normalized = resolveNormalizedText(candidate, null);
165
+ if (normalized) return normalized;
166
+ }
167
+ return null;
168
+ }
169
+
140
170
  function isAgentScopedSessionKey(sessionKey) {
141
171
  return /^agent:[^:]+:/i.test(String(sessionKey || ''));
142
172
  }
@@ -279,6 +309,7 @@ function parseBridgeTimestampMs(value) {
279
309
 
280
310
  function resolveBridgeDeliveryTimestampMs({ delivery = {}, metadata = {} } = {}) {
281
311
  return parseBridgeTimestampMs(delivery?.createdAt)
312
+ || parseBridgeTimestampMs(delivery?.availableAt)
282
313
  || parseBridgeTimestampMs(delivery?.turnCreatedAt)
283
314
  || parseBridgeTimestampMs(metadata?.createdAt)
284
315
  || Date.now();
@@ -1757,6 +1788,7 @@ function buildDeliveryInboundEnvelope({
1757
1788
  commandText = null,
1758
1789
  timestamp = null,
1759
1790
  deliveryId,
1791
+ eventType = 'delivery',
1760
1792
  sessionKey,
1761
1793
  localSessionKey = null,
1762
1794
  worldId = null,
@@ -1775,13 +1807,16 @@ function buildDeliveryInboundEnvelope({
1775
1807
  const normalizedCommandText = String(commandText || '').trim();
1776
1808
  const commandBody = normalizedCommandText || rawBody;
1777
1809
  const bodyForAgent = bodyText || rawBody;
1810
+ const eventLabel = normalizePluginOptionalText(eventType) === 'delivery'
1811
+ ? 'delivery'
1812
+ : `event ${normalizePluginOptionalText(eventType)}`;
1778
1813
  const contextLines = mergeUntrustedContextLines([
1779
1814
  `[claworld peer ${remoteLabel}]`,
1780
1815
  ...(worldId ? [`[claworld world ${worldId}]`] : []),
1781
1816
  ...(conversationKey ? [`[claworld conversation ${conversationKey}]`] : []),
1782
1817
  ...(localSessionKey && localSessionKey !== sessionKey ? [`[claworld local session ${localSessionKey}]`] : []),
1783
1818
  `[claworld relay session ${sessionKey}]`,
1784
- `[claworld delivery ${deliveryId}]`,
1819
+ `[claworld ${eventLabel} ${deliveryId}]`,
1785
1820
  ], untrustedContext);
1786
1821
  const envelopeTimestamp = Number.isFinite(timestamp) ? new Date(timestamp) : new Date();
1787
1822
 
@@ -1887,34 +1922,189 @@ function buildManagementRuntimeEventBody(details = {}, { localSessionKey = null
1887
1922
  return lines.join('\n');
1888
1923
  }
1889
1924
 
1890
- function resolveSessionRecordArtifacts(record = null, fallbackStorePath = null) {
1925
+ function normalizeSessionStoreKey(value) {
1926
+ return resolveNormalizedText(value, '').toLowerCase();
1927
+ }
1928
+
1929
+ function resolveSessionStoreEntry(store = null, sessionKey = null) {
1930
+ if (!store || typeof store !== 'object' || Array.isArray(store)) return null;
1931
+ const normalizedSessionKey = resolveNormalizedText(sessionKey, null);
1932
+ if (!normalizedSessionKey) return null;
1933
+ if (store[normalizedSessionKey] && typeof store[normalizedSessionKey] === 'object') {
1934
+ return store[normalizedSessionKey];
1935
+ }
1936
+ const lowerSessionKey = normalizeSessionStoreKey(normalizedSessionKey);
1937
+ if (store[lowerSessionKey] && typeof store[lowerSessionKey] === 'object') {
1938
+ return store[lowerSessionKey];
1939
+ }
1940
+ const match = Object.entries(store).find(([key, value]) => (
1941
+ normalizeSessionStoreKey(key) === lowerSessionKey
1942
+ && value
1943
+ && typeof value === 'object'
1944
+ && !Array.isArray(value)
1945
+ ));
1946
+ return match ? match[1] : null;
1947
+ }
1948
+
1949
+ function readRuntimeSessionStoreEntry({ runtime = null, sessionStorePath = null, sessionKey = null } = {}) {
1950
+ if (!runtime?.agent?.session?.loadSessionStore || !sessionStorePath || !sessionKey) return null;
1951
+ try {
1952
+ return resolveSessionStoreEntry(
1953
+ runtime.agent.session.loadSessionStore(sessionStorePath),
1954
+ sessionKey,
1955
+ );
1956
+ } catch {
1957
+ return null;
1958
+ }
1959
+ }
1960
+
1961
+ function resolveSessionFilePathFromRuntime({
1962
+ runtime = null,
1963
+ sessionId = null,
1964
+ record = {},
1965
+ sessionStorePath = null,
1966
+ localAgentId = null,
1967
+ } = {}) {
1968
+ const normalizedSessionId = resolveNormalizedText(sessionId, null);
1969
+ if (!normalizedSessionId) return null;
1970
+
1971
+ const sessionsDir = sessionStorePath
1972
+ ? path.dirname(path.resolve(sessionStorePath))
1973
+ : null;
1974
+ if (typeof runtime?.agent?.session?.resolveSessionFilePath === 'function') {
1975
+ try {
1976
+ const resolved = runtime.agent.session.resolveSessionFilePath(
1977
+ normalizedSessionId,
1978
+ record,
1979
+ {
1980
+ ...(sessionsDir ? { sessionsDir } : {}),
1981
+ ...(localAgentId ? { agentId: localAgentId } : {}),
1982
+ },
1983
+ );
1984
+ const normalized = resolveNormalizedText(resolved, null);
1985
+ if (normalized) return normalized;
1986
+ } catch {
1987
+ // Fall through to the local derivation below.
1988
+ }
1989
+ }
1990
+
1991
+ const candidate = resolveNormalizedText(record?.sessionFile, null);
1992
+ if (candidate) {
1993
+ if (path.isAbsolute(candidate) || !sessionsDir) return candidate;
1994
+ return path.resolve(sessionsDir, candidate);
1995
+ }
1996
+
1997
+ if (sessionsDir) {
1998
+ return path.join(sessionsDir, `${normalizedSessionId}.jsonl`);
1999
+ }
2000
+ return null;
2001
+ }
2002
+
2003
+ function resolveSessionRecordArtifacts(record = null, fallbackStorePath = null, options = {}) {
1891
2004
  const normalizedRecord = record && typeof record === 'object' && !Array.isArray(record) ? record : {};
1892
2005
  const nestedSession = normalizedRecord.session && typeof normalizedRecord.session === 'object' && !Array.isArray(normalizedRecord.session)
1893
2006
  ? normalizedRecord.session
1894
2007
  : {};
1895
- return {
1896
- sessionId: resolveNormalizedText(
1897
- normalizedRecord.sessionId,
1898
- resolveNormalizedText(nestedSession.sessionId, resolveNormalizedText(normalizedRecord.id, null)),
1899
- ),
1900
- sessionFile: resolveNormalizedText(
1901
- normalizedRecord.sessionFile,
2008
+ const sessionId = resolveNormalizedText(
2009
+ normalizedRecord.sessionId,
2010
+ resolveNormalizedText(nestedSession.sessionId, resolveNormalizedText(normalizedRecord.id, null)),
2011
+ );
2012
+ const sessionStorePath = resolveNormalizedText(
2013
+ normalizedRecord.storePath,
2014
+ resolveNormalizedText(normalizedRecord.sessionStorePath, fallbackStorePath),
2015
+ );
2016
+ const directSessionFile = resolveNormalizedText(
2017
+ normalizedRecord.sessionFile,
2018
+ resolveNormalizedText(
2019
+ normalizedRecord.sessionPath,
1902
2020
  resolveNormalizedText(
1903
- normalizedRecord.sessionPath,
1904
- resolveNormalizedText(
1905
- normalizedRecord.filePath,
1906
- resolveNormalizedText(normalizedRecord.path, resolveNormalizedText(nestedSession.filePath, null)),
1907
- ),
2021
+ normalizedRecord.filePath,
2022
+ resolveNormalizedText(normalizedRecord.path, resolveNormalizedText(nestedSession.filePath, null)),
1908
2023
  ),
1909
2024
  ),
1910
- sessionStorePath: resolveNormalizedText(
1911
- normalizedRecord.storePath,
1912
- resolveNormalizedText(normalizedRecord.sessionStorePath, fallbackStorePath),
1913
- ),
1914
- transcriptPath: resolveNormalizedText(
1915
- normalizedRecord.transcriptPath,
1916
- resolveNormalizedText(nestedSession.transcriptPath, null),
1917
- ),
2025
+ );
2026
+ const sessionFile = directSessionFile || resolveSessionFilePathFromRuntime({
2027
+ runtime: options.runtime,
2028
+ sessionId,
2029
+ record: normalizedRecord,
2030
+ sessionStorePath,
2031
+ localAgentId: options.localAgentId,
2032
+ });
2033
+ const transcriptPath = resolveNormalizedText(
2034
+ normalizedRecord.transcriptPath,
2035
+ resolveNormalizedText(nestedSession.transcriptPath, sessionFile),
2036
+ );
2037
+ return {
2038
+ sessionId,
2039
+ sessionFile,
2040
+ sessionStorePath,
2041
+ transcriptPath,
2042
+ };
2043
+ }
2044
+
2045
+ async function recordRuntimeInboundSessionArtifacts({
2046
+ runtime = null,
2047
+ currentCfg = {},
2048
+ localAgentId = null,
2049
+ sessionKey = null,
2050
+ ctx = {},
2051
+ logger = console,
2052
+ runtimeAccountId = null,
2053
+ logLabel = 'inbound',
2054
+ logContext = {},
2055
+ } = {}) {
2056
+ let sessionStorePath = null;
2057
+ let sessionRecord = null;
2058
+ const sessionApi = runtime?.channel?.session || {};
2059
+ const localSessionKey = resolveNormalizedText(ctx?.SessionKey, sessionKey);
2060
+
2061
+ if (sessionApi.resolveStorePath && localAgentId) {
2062
+ sessionStorePath = sessionApi.resolveStorePath(currentCfg.session?.store, {
2063
+ agentId: localAgentId,
2064
+ });
2065
+ const onRecordError = (error) => {
2066
+ logger.error?.(`[claworld:${runtimeAccountId}] failed to record ${logLabel} inbound session`, {
2067
+ ...logContext,
2068
+ sessionKey,
2069
+ localSessionKey,
2070
+ localAgentId,
2071
+ error: error?.message || String(error),
2072
+ });
2073
+ };
2074
+ try {
2075
+ if (typeof sessionApi.recordSessionMetaFromInbound === 'function') {
2076
+ sessionRecord = await sessionApi.recordSessionMetaFromInbound({
2077
+ storePath: sessionStorePath,
2078
+ sessionKey: localSessionKey,
2079
+ ctx,
2080
+ });
2081
+ } else if (typeof sessionApi.recordInboundSession === 'function') {
2082
+ sessionRecord = await sessionApi.recordInboundSession({
2083
+ storePath: sessionStorePath,
2084
+ sessionKey: localSessionKey,
2085
+ ctx,
2086
+ onRecordError,
2087
+ });
2088
+ }
2089
+ } catch (error) {
2090
+ onRecordError(error);
2091
+ }
2092
+ if (!sessionRecord) {
2093
+ sessionRecord = readRuntimeSessionStoreEntry({
2094
+ runtime,
2095
+ sessionStorePath,
2096
+ sessionKey: localSessionKey,
2097
+ });
2098
+ }
2099
+ }
2100
+
2101
+ return {
2102
+ sessionStorePath,
2103
+ sessionRecord,
2104
+ sessionArtifacts: resolveSessionRecordArtifacts(sessionRecord, sessionStorePath, {
2105
+ runtime,
2106
+ localAgentId,
2107
+ }),
1918
2108
  };
1919
2109
  }
1920
2110
 
@@ -2402,7 +2592,7 @@ function resolveBoundLocalAgentId({ cfg = {}, runtimeConfig = {}, relayClient }
2402
2592
  || 'main';
2403
2593
  }
2404
2594
 
2405
- async function maybeBridgeRuntimeDelivery({
2595
+ async function maybeBridgeRuntimeInboundEvent({
2406
2596
  relayClient,
2407
2597
  runtimeConfig,
2408
2598
  runtimeAccountId,
@@ -2421,36 +2611,49 @@ async function maybeBridgeRuntimeDelivery({
2421
2611
  const payload = delivery.payload && typeof delivery.payload === 'object' && !Array.isArray(delivery.payload)
2422
2612
  ? delivery.payload
2423
2613
  : {};
2424
- const deliveryId = resolveNormalizedText(delivery.deliveryId, null);
2614
+ const eventType = resolveNormalizedText(delivery.eventType, resolveNormalizedText(event?.eventType, 'delivery'));
2615
+ const deliveryId = resolveInboundMessageId({ delivery, payload, metadata });
2425
2616
  const sessionKey = resolveNormalizedText(delivery.sessionKey, null);
2617
+ const fallbackRuntimeEventText = eventType !== 'delivery'
2618
+ ? buildManagementRuntimeEventBody(resolveRuntimeManagementEnvelope(event))
2619
+ : null;
2426
2620
  const contextText = resolveNormalizedText(payload.contextText, null);
2427
2621
  const incomingText = resolveNormalizedText(
2428
2622
  payload.commandText,
2429
- contextText ? null : resolveNormalizedText(payload.text, null),
2623
+ contextText ? null : resolveNormalizedText(payload.text, resolveNormalizedText(payload.body, fallbackRuntimeEventText)),
2430
2624
  );
2431
2625
  const commandText = resolveNormalizedText(payload.commandText, incomingText);
2432
2626
  const fromAgentId = resolveNormalizedText(metadata.fromAgentId, null);
2433
- const remoteIdentity = fromAgentId || 'unknown-peer';
2627
+ const routeSessionKind = resolveNormalizedText(
2628
+ event?.route?.sessionKind,
2629
+ resolveNormalizedText(delivery.sessionKind, resolveNormalizedText(payload.sessionKind, null)),
2630
+ );
2631
+ const isRelayDelivery = eventType === 'delivery';
2632
+ const allowReply = metadata.allowReply === true || (isRelayDelivery && metadata.allowReply !== false);
2633
+ const remoteIdentity = fromAgentId
2634
+ || resolveNormalizedText(metadata.source, routeSessionKind === 'management' ? 'claworld-management' : 'unknown-peer');
2434
2635
 
2435
2636
  if (
2436
2637
  !runtime?.channel?.reply?.finalizeInboundContext
2437
2638
  || !runtime?.channel?.reply?.dispatchReplyFromConfig
2438
2639
  || !runtime?.channel?.reply?.createReplyDispatcherWithTyping
2439
2640
  ) {
2440
- logger.warn?.(`[claworld:${runtimeAccountId}] skipping delivery bridge: missing runtime bridge hooks`, {
2641
+ logger.warn?.(`[claworld:${runtimeAccountId}] skipping inbound bridge: missing runtime bridge hooks`, {
2642
+ eventType,
2441
2643
  deliveryId,
2442
2644
  sessionKey,
2443
2645
  });
2444
2646
  return { skipped: true, reason: 'missing_runtime_bridge_hooks' };
2445
2647
  }
2446
2648
  if (!deliveryId || !sessionKey || (!incomingText && !contextText)) {
2447
- logger.warn?.(`[claworld:${runtimeAccountId}] skipping delivery bridge: missing delivery payload`, {
2649
+ logger.warn?.(`[claworld:${runtimeAccountId}] skipping inbound bridge: missing payload`, {
2650
+ eventType,
2448
2651
  deliveryId,
2449
2652
  sessionKey,
2450
2653
  hasIncomingText: Boolean(incomingText),
2451
2654
  hasContextText: Boolean(contextText),
2452
2655
  });
2453
- return { skipped: true, reason: 'missing_delivery_payload' };
2656
+ return { skipped: true, reason: 'missing_inbound_payload' };
2454
2657
  }
2455
2658
 
2456
2659
  const loadedCfg = await runtime.config?.loadConfig?.() || {};
@@ -2476,7 +2679,7 @@ async function maybeBridgeRuntimeDelivery({
2476
2679
  fallbackTarget: runtimeConfig.routing?.fallbackTarget,
2477
2680
  }) || null;
2478
2681
  const worldId = resolveDeliveryWorldId(delivery);
2479
- const commandAuthorized = shouldAuthorizeBridgedCommand({
2682
+ const commandAuthorized = isRelayDelivery && shouldAuthorizeBridgedCommand({
2480
2683
  runtimeConfig,
2481
2684
  incomingText: commandText || incomingText,
2482
2685
  });
@@ -2490,6 +2693,7 @@ async function maybeBridgeRuntimeDelivery({
2490
2693
  commandText,
2491
2694
  timestamp: inboundTimestamp,
2492
2695
  deliveryId,
2696
+ eventType,
2493
2697
  sessionKey,
2494
2698
  localSessionKey,
2495
2699
  worldId,
@@ -2497,6 +2701,9 @@ async function maybeBridgeRuntimeDelivery({
2497
2701
  untrustedContext: payload.untrustedContext,
2498
2702
  });
2499
2703
  const localIdentity = normalizeClaworldText(runtimeConfig.relay?.agentId, runtimeConfig.accountId);
2704
+ const isManagementSession = routeSessionKind === 'management';
2705
+ const senderName = isManagementSession ? 'Claworld' : remoteIdentity;
2706
+ const conversationLabel = isManagementSession ? 'Claworld management' : remoteIdentity;
2500
2707
  const inboundCtx = runtime.channel.reply.finalizeInboundContext({
2501
2708
  Body,
2502
2709
  RawBody,
@@ -2511,45 +2718,38 @@ async function maybeBridgeRuntimeDelivery({
2511
2718
  OriginatingChannel: 'claworld',
2512
2719
  OriginatingFrom: remoteIdentity,
2513
2720
  OriginatingTo: remoteIdentity,
2514
- ChatType: 'direct',
2515
- SenderName: remoteIdentity,
2721
+ ChatType: isManagementSession ? 'management' : 'direct',
2722
+ SessionType: isManagementSession ? 'management' : 'direct',
2723
+ sessionType: isManagementSession ? 'management' : 'direct',
2724
+ sessionKind: isManagementSession ? 'management' : 'conversation',
2725
+ SenderName: senderName,
2516
2726
  SenderId: remoteIdentity,
2517
2727
  MessageId: deliveryId,
2518
2728
  Provider: 'claworld',
2519
2729
  Surface: 'claworld',
2520
- ConversationLabel: remoteIdentity,
2730
+ ConversationLabel: conversationLabel,
2521
2731
  Timestamp: inboundTimestamp,
2522
2732
  MessageSid: deliveryId,
2523
2733
  WasMentioned: false,
2524
2734
  CommandAuthorized: commandAuthorized,
2525
- RelayDeliveryId: deliveryId,
2735
+ RelayDeliveryId: isRelayDelivery ? deliveryId : null,
2526
2736
  RelayFromAgentId: fromAgentId,
2527
2737
  UntrustedContext,
2528
2738
  });
2529
2739
 
2530
- let sessionStorePath = null;
2531
- let sessionRecord = null;
2532
- let sessionArtifacts = {};
2533
- if (runtime?.channel?.session?.recordInboundSession && runtime?.channel?.session?.resolveStorePath && localAgentId) {
2534
- sessionStorePath = runtime.channel.session.resolveStorePath(currentCfg.session?.store, {
2535
- agentId: localAgentId,
2536
- });
2537
- sessionRecord = await runtime.channel.session.recordInboundSession({
2538
- storePath: sessionStorePath,
2539
- sessionKey: inboundCtx.SessionKey || sessionKey,
2540
- ctx: inboundCtx,
2541
- onRecordError: (error) => {
2542
- logger.error?.(`[claworld:${runtimeAccountId}] failed to record inbound session`, {
2543
- deliveryId,
2544
- sessionKey,
2545
- localSessionKey,
2546
- localAgentId,
2547
- error: error?.message || String(error),
2548
- });
2549
- },
2550
- });
2551
- sessionArtifacts = resolveSessionRecordArtifacts(sessionRecord, sessionStorePath);
2552
- }
2740
+ const {
2741
+ sessionArtifacts,
2742
+ } = await recordRuntimeInboundSessionArtifacts({
2743
+ runtime,
2744
+ currentCfg,
2745
+ localAgentId,
2746
+ sessionKey,
2747
+ ctx: inboundCtx,
2748
+ logger,
2749
+ runtimeAccountId,
2750
+ logLabel: isRelayDelivery ? 'delivery' : 'inbound',
2751
+ logContext: { eventType, deliveryId },
2752
+ });
2553
2753
 
2554
2754
  const workspaceRoot = resolveOpenClawWorkspaceRoot({
2555
2755
  sources: [
@@ -2563,9 +2763,8 @@ async function maybeBridgeRuntimeDelivery({
2563
2763
  let journalResult = null;
2564
2764
  if (workspaceRoot) {
2565
2765
  try {
2566
- journalResult = await appendClaworldJournalEvent(
2567
- workspaceRoot,
2568
- buildDeliveryRuntimeMaintenanceEvent({
2766
+ const maintenanceEvent = isRelayDelivery
2767
+ ? buildDeliveryRuntimeMaintenanceEvent({
2569
2768
  delivery,
2570
2769
  metadata,
2571
2770
  payload,
@@ -2573,14 +2772,24 @@ async function maybeBridgeRuntimeDelivery({
2573
2772
  localAgentId,
2574
2773
  sessionArtifacts,
2575
2774
  workspaceRoot,
2576
- }),
2775
+ })
2776
+ : buildManagementRuntimeMaintenanceEvent(resolveRuntimeManagementEnvelope(event), {
2777
+ localSessionKey,
2778
+ localAgentId,
2779
+ sessionArtifacts,
2780
+ workspaceRoot,
2781
+ });
2782
+ journalResult = await appendClaworldJournalEvent(
2783
+ workspaceRoot,
2784
+ maintenanceEvent,
2577
2785
  {
2578
- trigger: 'delivery',
2579
- rule: 'runtime_delivery_joined_session',
2786
+ trigger: isRelayDelivery ? 'delivery' : 'runtime_event',
2787
+ rule: isRelayDelivery ? 'runtime_delivery_joined_session' : 'runtime_event_joined_session',
2580
2788
  },
2581
2789
  );
2582
2790
  } catch (error) {
2583
- logger.warn?.(`[claworld:${runtimeAccountId}] delivery journal append failed`, {
2791
+ logger.warn?.(`[claworld:${runtimeAccountId}] inbound journal append failed`, {
2792
+ eventType,
2584
2793
  deliveryId,
2585
2794
  sessionKey,
2586
2795
  localSessionKey,
@@ -2590,7 +2799,8 @@ async function maybeBridgeRuntimeDelivery({
2590
2799
  }
2591
2800
  }
2592
2801
 
2593
- logger.info?.(`[claworld:${runtimeAccountId}] routing delivery into runtime session`, {
2802
+ logger.info?.(`[claworld:${runtimeAccountId}] ${isRelayDelivery ? 'routing delivery into runtime session' : 'routing inbound event into runtime session'}`, {
2803
+ eventType,
2594
2804
  deliveryId,
2595
2805
  sessionKey,
2596
2806
  localSessionKey,
@@ -2601,28 +2811,30 @@ async function maybeBridgeRuntimeDelivery({
2601
2811
  routeStatus: routed?.status || null,
2602
2812
  bodyPreview: String(Body || '').slice(0, 240),
2603
2813
  rawBodyPreview: String(RawBody || '').slice(0, 240),
2604
- allowReply: metadata.allowReply !== false,
2814
+ allowReply,
2605
2815
  commandAuthorized,
2606
2816
  journal: journalResult?.ok === true,
2607
2817
  });
2608
2818
 
2609
- try {
2610
- const acceptedResult = await relayClient.acceptDeliveryHttp({
2611
- deliveryId,
2612
- sessionKey,
2613
- source: 'runtime_dispatch',
2614
- });
2615
- if (acceptedResult.status < 200 || acceptedResult.status >= 300) {
2616
- throw new Error(`failed to submit relay delivery acceptance: ${acceptedResult.status}`);
2819
+ if (isRelayDelivery && metadata.acceptanceRequired !== false) {
2820
+ try {
2821
+ const acceptedResult = await relayClient.acceptDeliveryHttp({
2822
+ deliveryId,
2823
+ sessionKey,
2824
+ source: 'runtime_dispatch',
2825
+ });
2826
+ if (acceptedResult.status < 200 || acceptedResult.status >= 300) {
2827
+ throw new Error(`failed to submit relay delivery acceptance: ${acceptedResult.status}`);
2828
+ }
2829
+ } catch (error) {
2830
+ logger.warn?.(`[claworld:${runtimeAccountId}] delivery acceptance acknowledgement failed`, {
2831
+ deliveryId,
2832
+ sessionKey,
2833
+ localSessionKey,
2834
+ localAgentId,
2835
+ error: error?.message || String(error),
2836
+ });
2617
2837
  }
2618
- } catch (error) {
2619
- logger.warn?.(`[claworld:${runtimeAccountId}] delivery acceptance acknowledgement failed`, {
2620
- deliveryId,
2621
- sessionKey,
2622
- localSessionKey,
2623
- localAgentId,
2624
- error: error?.message || String(error),
2625
- });
2626
2838
  }
2627
2839
 
2628
2840
  let {
@@ -2637,15 +2849,16 @@ async function maybeBridgeRuntimeDelivery({
2637
2849
  deliveryId,
2638
2850
  sessionKey,
2639
2851
  localAgentId,
2640
- allowReply: metadata.allowReply !== false,
2852
+ allowReply,
2641
2853
  logger,
2642
2854
  runtimeAccountId,
2643
2855
  inboundCtx,
2644
2856
  });
2645
2857
 
2646
2858
  const shouldRetryKickoffDispatch = (
2647
- metadata.deliveryType === 'kickoff'
2648
- && metadata.allowReply !== false
2859
+ isRelayDelivery
2860
+ && metadata.deliveryType === 'kickoff'
2861
+ && allowReply
2649
2862
  && replied !== true
2650
2863
  && runtimeOutputSummary.counts.final > 0
2651
2864
  && runtimeOutputSummary.counts.nonRenderableFinal > 0
@@ -2682,14 +2895,15 @@ async function maybeBridgeRuntimeDelivery({
2682
2895
  deliveryId,
2683
2896
  sessionKey,
2684
2897
  localAgentId,
2685
- allowReply: metadata.allowReply !== false,
2898
+ allowReply,
2686
2899
  logger,
2687
2900
  runtimeAccountId,
2688
2901
  inboundCtx,
2689
2902
  }));
2690
2903
  }
2691
2904
 
2692
- logger.info?.(`[claworld:${runtimeAccountId}] delivery bridge completed`, {
2905
+ logger.info?.(`[claworld:${runtimeAccountId}] ${isRelayDelivery ? 'delivery bridge completed' : 'inbound bridge completed'}`, {
2906
+ eventType,
2693
2907
  deliveryId,
2694
2908
  sessionKey,
2695
2909
  localSessionKey,
@@ -2806,30 +3020,23 @@ async function maybeBridgeRuntimeManagementEvent({
2806
3020
  RelayTargetAgentId: details.targetAgentId,
2807
3021
  });
2808
3022
 
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
- }
3023
+ const {
3024
+ sessionStorePath,
3025
+ sessionArtifacts,
3026
+ } = await recordRuntimeInboundSessionArtifacts({
3027
+ runtime,
3028
+ currentCfg,
3029
+ localAgentId,
3030
+ sessionKey,
3031
+ ctx: inboundCtx,
3032
+ logger,
3033
+ runtimeAccountId,
3034
+ logLabel: 'management',
3035
+ logContext: {
3036
+ eventType: details.eventType,
3037
+ eventName: details.eventName,
3038
+ },
3039
+ });
2833
3040
 
2834
3041
  const workspaceRoot = resolveOpenClawWorkspaceRoot({
2835
3042
  sources: [
@@ -3350,9 +3557,9 @@ export function createClaworldChannelPlugin({
3350
3557
  sessionKey: event?.delivery?.sessionKey || null,
3351
3558
  });
3352
3559
 
3353
- if (event?.eventType === 'delivery') {
3560
+ if (event?.delivery?.sessionKey) {
3354
3561
  const runtimeContext = accountRuntimeContexts.get(accountKey) || {};
3355
- maybeBridgeRuntimeDelivery({
3562
+ maybeBridgeRuntimeInboundEvent({
3356
3563
  relayClient,
3357
3564
  runtimeConfig,
3358
3565
  runtimeAccountId,
@@ -3362,7 +3569,7 @@ export function createClaworldChannelPlugin({
3362
3569
  cfg: runtimeContext.cfg,
3363
3570
  inbound,
3364
3571
  }).catch((error) => {
3365
- logger.error?.(`[claworld:${runtimeAccountId}] delivery bridge exception`, {
3572
+ logger.error?.(`[claworld:${runtimeAccountId}] inbound bridge exception`, {
3366
3573
  error: error?.message || String(error),
3367
3574
  });
3368
3575
  });
@@ -3523,7 +3730,8 @@ export function createClaworldChannelPlugin({
3523
3730
  }
3524
3731
 
3525
3732
  async function updateRuntimePublicIdentity(context = {}) {
3526
- const resolvedContext = resolveConfiguredRuntimeContext(context);
3733
+ const configuredContext = resolveConfiguredRuntimeContext(context);
3734
+ const resolvedContext = await resolveBoundRuntimeContext(context);
3527
3735
  const updateResult = await updatePublicIdentity({
3528
3736
  runtimeConfig: resolvedContext.runtimeConfig,
3529
3737
  agentId: resolvedContext.agentId || null,
@@ -3553,14 +3761,20 @@ async function updateRuntimePublicIdentity(context = {}) {
3553
3761
  ? applyRuntimeIdentity(nextRuntimeConfig, { agentId: nextAgentId })
3554
3762
  : nextRuntimeConfig;
3555
3763
 
3764
+ const configuredAppToken = resolveRuntimeAppToken(configuredContext.runtimeConfig);
3556
3765
  const previousAgentId = normalizeClaworldText(
3557
- resolvedContext.runtimeConfig?.relay?.agentId,
3558
- normalizeClaworldText(resolvedContext.agentId, null),
3766
+ configuredContext.runtimeConfig?.relay?.agentId,
3767
+ normalizeClaworldText(configuredContext.agentId, null),
3559
3768
  );
3769
+ const nextAppToken = resolveRuntimeAppToken(boundRuntimeConfig);
3560
3770
  const shouldPersistRuntimeBinding = Boolean(
3561
- resolveRuntimeAppToken(nextRuntimeConfig)
3771
+ nextAppToken
3562
3772
  && nextAgentId
3563
- && (runtimeActivation || previousAgentId !== nextAgentId),
3773
+ && (
3774
+ runtimeActivation
3775
+ || configuredAppToken !== nextAppToken
3776
+ || previousAgentId !== nextAgentId
3777
+ ),
3564
3778
  );
3565
3779
 
3566
3780
  if (shouldPersistRuntimeBinding) {
@@ -3569,7 +3783,7 @@ async function updateRuntimePublicIdentity(context = {}) {
3569
3783
  await persistRuntimeAppToken({
3570
3784
  runtime: runtimeResolution.runtime,
3571
3785
  accountId: resolvedContext.accountId || boundRuntimeConfig.accountId || null,
3572
- appToken: resolveRuntimeAppToken(boundRuntimeConfig),
3786
+ appToken: nextAppToken,
3573
3787
  relayAgentId: nextAgentId,
3574
3788
  });
3575
3789
  } catch (error) {
@@ -3582,7 +3796,9 @@ async function updateRuntimePublicIdentity(context = {}) {
3582
3796
  rememberAccountBinding({
3583
3797
  runtimeConfig: boundRuntimeConfig,
3584
3798
  accountId: resolvedContext.accountId || boundRuntimeConfig.accountId || null,
3585
- bindingSource: runtimeActivation ? 'activated_app_token' : 'configured_app_token',
3799
+ bindingSource: runtimeActivation
3800
+ ? 'activated_app_token'
3801
+ : (resolvedContext.bindingSource || 'configured_app_token'),
3586
3802
  });
3587
3803
 
3588
3804
  const accountKey = resolveAccountBindingKey(boundRuntimeConfig, resolvedContext.accountId || null);