nemo-relay-openclaw 0.8.4 → 0.9.0-rc.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,10 +8,11 @@ import type * as NemoRelayRuntime from 'nemo-relay-node';
8
8
  import type * as NemoRelayAdaptive from 'nemo-relay-node/adaptive';
9
9
  import type * as NemoRelayPluginHost from 'nemo-relay-node/plugin';
10
10
  type NemoRelayRuntimeKeys = 'ScopeType' | 'createScopeStack' | 'currentScopeStack' | 'setThreadScopeStack' | 'pushScope' | 'popScope' | 'event' | 'llmCall' | 'llmCallEnd' | 'toolCall' | 'toolCallEnd' | 'toolConditionalExecution';
11
- type NemoRelayPluginHostKeys = 'defaultConfig' | 'validate' | 'initialize' | 'clear';
11
+ type NemoRelayPluginHostKeys = 'defaultConfig' | 'validate' | 'initialize';
12
12
  type NemoRelayAdaptiveKeys = 'ADAPTIVE_PLUGIN_KIND' | 'ComponentSpec';
13
13
  export type ConfigDiagnostic = NemoRelayPluginHost.ConfigDiagnostic;
14
14
  export type ConfigReport = NemoRelayPluginHost.ConfigReport;
15
+ export type PluginHostActivation = NemoRelayPluginHost.PluginHostActivation;
15
16
  /**
16
17
  * @internal Package-owned subset of the dynamically imported `nemo-relay-node`
17
18
  * namespace used by this integration.
@@ -19,7 +19,7 @@ export declare class NemoRelayRuntimeState {
19
19
  private loadPromise;
20
20
  private startPromise;
21
21
  private statusValue;
22
- private modulesValue?;
22
+ private pluginHostActivation;
23
23
  private backendValue;
24
24
  private initializedPluginHost;
25
25
  private pluginHostOutputsHealthy;
@@ -15,7 +15,7 @@ export class NemoRelayRuntimeState {
15
15
  loadPromise;
16
16
  startPromise;
17
17
  statusValue = { state: 'not_initialized' };
18
- modulesValue;
18
+ pluginHostActivation;
19
19
  backendValue;
20
20
  initializedPluginHost = false;
21
21
  pluginHostOutputsHealthy = false;
@@ -79,7 +79,6 @@ export class NemoRelayRuntimeState {
79
79
  try {
80
80
  this.loadPromise ??= this.moduleLoader();
81
81
  modules = await this.loadPromise;
82
- this.modulesValue = modules;
83
82
  }
84
83
  catch (error) {
85
84
  this.loadPromise = undefined;
@@ -97,19 +96,20 @@ export class NemoRelayRuntimeState {
97
96
  degradedReason = validationReport.reason;
98
97
  ctx.logger.warn?.(degradedReason);
99
98
  }
100
- else if (validationReport.report.diagnostics.some((diagnostic) => diagnostic.level === 'error')) {
99
+ else if (validationReport.report.config.diagnostics.some((diagnostic) => diagnostic.level === 'error')) {
101
100
  degradedReason = 'NeMo Relay plugin host config validation failed';
102
101
  }
103
102
  else {
104
- if (validationReport.report.diagnostics.some((diagnostic) => diagnostic.level === 'warning') &&
103
+ if (validationReport.report.config.diagnostics.some((diagnostic) => diagnostic.level === 'warning') &&
105
104
  degradedReason === undefined) {
106
105
  degradedReason = 'NeMo Relay plugin host config validation produced warnings';
107
106
  }
108
107
  try {
109
- const activationReport = await modules.pluginHost.initialize(hostConfig);
110
- logDiagnostics(ctx.logger, activationReport.diagnostics);
108
+ this.pluginHostActivation = await modules.pluginHost.initialize(hostConfig);
109
+ const activationDiagnostics = this.pluginHostActivation.report.config.diagnostics;
110
+ logDiagnostics(ctx.logger, activationDiagnostics);
111
111
  this.initializedPluginHost = true;
112
- const hasInitializationErrors = activationReport.diagnostics.some((diagnostic) => diagnostic.level === 'error');
112
+ const hasInitializationErrors = activationDiagnostics.some((diagnostic) => diagnostic.level === 'error');
113
113
  this.pluginHostOutputsHealthy = !hasInitializationErrors;
114
114
  if (hasInitializationErrors) {
115
115
  degradedReason ??= 'NeMo Relay plugin host initialization reported errors';
@@ -175,18 +175,24 @@ export class NemoRelayRuntimeState {
175
175
  this.lastCounters = { ...backendState.counters };
176
176
  }
177
177
  this.backendValue = undefined;
178
- if (this.initializedPluginHost && this.modulesValue) {
178
+ let pluginHostCloseFailure;
179
+ if (this.pluginHostActivation) {
179
180
  try {
180
- this.modulesValue.pluginHost.clear();
181
+ await this.pluginHostActivation.close();
181
182
  }
182
183
  catch (error) {
183
- log.warn?.(`failed to clear NeMo Relay plugin host: ${toMessage(error)}`);
184
+ pluginHostCloseFailure = `failed to close NeMo Relay plugin host: ${toMessage(error)}`;
185
+ log.warn?.(pluginHostCloseFailure);
186
+ }
187
+ if (pluginHostCloseFailure === undefined) {
188
+ this.pluginHostActivation = undefined;
189
+ this.initializedPluginHost = false;
190
+ this.pluginHostOutputsHealthy = false;
184
191
  }
185
- this.initializedPluginHost = false;
186
- this.pluginHostOutputsHealthy = false;
187
192
  }
188
193
  this.started = false;
189
- this.statusValue = finalStatus;
194
+ this.statusValue =
195
+ pluginHostCloseFailure === undefined ? finalStatus : { state: 'degraded', reason: pluginHostCloseFailure };
190
196
  }
191
197
  /** Handle OpenClaw runtime lifecycle cleanup for either a session or the backend. */
192
198
  async cleanup(ctx) {
@@ -381,7 +387,7 @@ function registerToolCallMiddleware(api, runtime) {
381
387
  function validatePluginHostConfig(modules, config, logger) {
382
388
  try {
383
389
  const report = modules.pluginHost.validate(config);
384
- logDiagnostics(logger, report.diagnostics);
390
+ logDiagnostics(logger, report.config.diagnostics);
385
391
  return { ok: true, report };
386
392
  }
387
393
  catch (error) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nemo-relay-openclaw",
3
- "version": "0.8.4",
3
+ "version": "0.9.0-rc.2",
4
4
  "description": "NeMo Relay-authored observability plugin for OpenClaw.",
5
5
  "type": "module",
6
6
  "repository": {
@@ -42,12 +42,12 @@
42
42
  "./dist/index.js"
43
43
  ],
44
44
  "compat": {
45
- "pluginApi": ">=2026.7.1-2",
46
- "minGatewayVersion": "2026.7.1-2"
45
+ "pluginApi": ">=2026.9.3",
46
+ "minGatewayVersion": "2026.9.3"
47
47
  },
48
48
  "build": {
49
- "openclawVersion": "2026.7.1-2",
50
- "pluginSdkVersion": "2026.7.1-2"
49
+ "openclawVersion": "2026.9.3",
50
+ "pluginSdkVersion": "2026.9.3"
51
51
  }
52
52
  },
53
53
  "scripts": {
@@ -60,13 +60,19 @@
60
60
  "test:live": "npm run build:test && node scripts/test-live.mjs"
61
61
  },
62
62
  "peerDependencies": {
63
- "openclaw": "2026.7.1-2"
63
+ "openclaw": "2026.9.3"
64
+ },
65
+ "peerDependenciesMeta": {
66
+ "openclaw": {
67
+ "optional": true
68
+ }
64
69
  },
65
70
  "dependencies": {
66
- "nemo-relay-node": "0.8.4"
71
+ "nemo-relay-node": "0.9.0-rc.2"
67
72
  },
68
73
  "devDependencies": {
69
74
  "@types/node": "^24.0.0",
75
+ "openclaw": "2026.9.3",
70
76
  "typescript": "^5.8.2"
71
77
  },
72
78
  "license": "Apache-2.0"
package/src/modules.ts CHANGED
@@ -25,11 +25,12 @@ type NemoRelayRuntimeKeys =
25
25
  | 'toolCallEnd'
26
26
  | 'toolConditionalExecution';
27
27
 
28
- type NemoRelayPluginHostKeys = 'defaultConfig' | 'validate' | 'initialize' | 'clear';
28
+ type NemoRelayPluginHostKeys = 'defaultConfig' | 'validate' | 'initialize';
29
29
  type NemoRelayAdaptiveKeys = 'ADAPTIVE_PLUGIN_KIND' | 'ComponentSpec';
30
30
 
31
31
  export type ConfigDiagnostic = NemoRelayPluginHost.ConfigDiagnostic;
32
32
  export type ConfigReport = NemoRelayPluginHost.ConfigReport;
33
+ export type PluginHostActivation = NemoRelayPluginHost.PluginHostActivation;
33
34
 
34
35
  /**
35
36
  * @internal Package-owned subset of the dynamically imported `nemo-relay-node`
@@ -26,6 +26,7 @@ import {
26
26
  type ConfigDiagnostic,
27
27
  type NemoRelayModules,
28
28
  type NemoRelayModuleLoader,
29
+ type PluginHostActivation,
29
30
  } from './modules.js';
30
31
  import type { RuntimeStateOptions, StartContext } from './types.js';
31
32
 
@@ -52,7 +53,7 @@ export class NemoRelayRuntimeState {
52
53
  private loadPromise: Promise<NemoRelayModules> | undefined;
53
54
  private startPromise: Promise<void> | undefined;
54
55
  private statusValue: HookReplayBackendStatus = { state: 'not_initialized' };
55
- private modulesValue?: NemoRelayModules;
56
+ private pluginHostActivation: PluginHostActivation | undefined;
56
57
  private backendValue: HookReplayBackend | undefined;
57
58
  private initializedPluginHost = false;
58
59
  private pluginHostOutputsHealthy = false;
@@ -124,7 +125,6 @@ export class NemoRelayRuntimeState {
124
125
  try {
125
126
  this.loadPromise ??= this.moduleLoader();
126
127
  modules = await this.loadPromise;
127
- this.modulesValue = modules;
128
128
  } catch (error) {
129
129
  this.loadPromise = undefined;
130
130
  this.statusValue = { state: 'degraded', reason: `failed to load nemo-relay-node: ${toMessage(error)}` };
@@ -143,21 +143,22 @@ export class NemoRelayRuntimeState {
143
143
  if (!validationReport.ok) {
144
144
  degradedReason = validationReport.reason;
145
145
  ctx.logger.warn?.(degradedReason);
146
- } else if (validationReport.report.diagnostics.some((diagnostic) => diagnostic.level === 'error')) {
146
+ } else if (validationReport.report.config.diagnostics.some((diagnostic) => diagnostic.level === 'error')) {
147
147
  degradedReason = 'NeMo Relay plugin host config validation failed';
148
148
  } else {
149
149
  if (
150
- validationReport.report.diagnostics.some((diagnostic) => diagnostic.level === 'warning') &&
150
+ validationReport.report.config.diagnostics.some((diagnostic) => diagnostic.level === 'warning') &&
151
151
  degradedReason === undefined
152
152
  ) {
153
153
  degradedReason = 'NeMo Relay plugin host config validation produced warnings';
154
154
  }
155
155
 
156
156
  try {
157
- const activationReport = await modules.pluginHost.initialize(hostConfig);
158
- logDiagnostics(ctx.logger, activationReport.diagnostics);
157
+ this.pluginHostActivation = await modules.pluginHost.initialize(hostConfig);
158
+ const activationDiagnostics = this.pluginHostActivation.report.config.diagnostics;
159
+ logDiagnostics(ctx.logger, activationDiagnostics);
159
160
  this.initializedPluginHost = true;
160
- const hasInitializationErrors = activationReport.diagnostics.some((diagnostic) => diagnostic.level === 'error');
161
+ const hasInitializationErrors = activationDiagnostics.some((diagnostic) => diagnostic.level === 'error');
161
162
  this.pluginHostOutputsHealthy = !hasInitializationErrors;
162
163
  if (hasInitializationErrors) {
163
164
  degradedReason ??= 'NeMo Relay plugin host initialization reported errors';
@@ -238,18 +239,24 @@ export class NemoRelayRuntimeState {
238
239
  }
239
240
  this.backendValue = undefined;
240
241
 
241
- if (this.initializedPluginHost && this.modulesValue) {
242
+ let pluginHostCloseFailure: string | undefined;
243
+ if (this.pluginHostActivation) {
242
244
  try {
243
- this.modulesValue.pluginHost.clear();
245
+ await this.pluginHostActivation.close();
244
246
  } catch (error) {
245
- log.warn?.(`failed to clear NeMo Relay plugin host: ${toMessage(error)}`);
247
+ pluginHostCloseFailure = `failed to close NeMo Relay plugin host: ${toMessage(error)}`;
248
+ log.warn?.(pluginHostCloseFailure);
249
+ }
250
+ if (pluginHostCloseFailure === undefined) {
251
+ this.pluginHostActivation = undefined;
252
+ this.initializedPluginHost = false;
253
+ this.pluginHostOutputsHealthy = false;
246
254
  }
247
- this.initializedPluginHost = false;
248
- this.pluginHostOutputsHealthy = false;
249
255
  }
250
256
 
251
257
  this.started = false;
252
- this.statusValue = finalStatus;
258
+ this.statusValue =
259
+ pluginHostCloseFailure === undefined ? finalStatus : { state: 'degraded', reason: pluginHostCloseFailure };
253
260
  }
254
261
 
255
262
  /** Handle OpenClaw runtime lifecycle cleanup for either a session or the backend. */
@@ -513,7 +520,7 @@ function validatePluginHostConfig(
513
520
  ): { ok: true; report: ReturnType<NemoRelayModules['pluginHost']['validate']> } | { ok: false; reason: string } {
514
521
  try {
515
522
  const report = modules.pluginHost.validate(config);
516
- logDiagnostics(logger, report.diagnostics);
523
+ logDiagnostics(logger, report.config.diagnostics);
517
524
  return { ok: true, report };
518
525
  } catch (error) {
519
526
  return {