nemo-relay-openclaw 0.6.0 → 0.7.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.
- package/README.md +29 -25
- package/dist/index.js +1 -1
- package/dist/openclaw.plugin.json +2 -2
- package/dist/src/config.js +1 -1
- package/dist/src/health.js +7 -2
- package/dist/src/hooks-backend.js +3 -3
- package/index.ts +1 -1
- package/openclaw.plugin.json +2 -2
- package/package.json +2 -2
- package/src/config.ts +1 -1
- package/src/health.ts +7 -2
- package/src/hooks-backend.ts +3 -3
package/README.md
CHANGED
|
@@ -8,8 +8,8 @@ SPDX-License-Identifier: Apache-2.0
|
|
|
8
8
|
`nemo-relay-openclaw` is the NeMo Relay observability plugin package for
|
|
9
9
|
OpenClaw. It converts supported OpenClaw hook events into NeMo Relay sessions,
|
|
10
10
|
LLM spans, tool spans, and lifecycle marks that the generic NeMo Relay
|
|
11
|
-
observability component can export as ATIF JSON
|
|
12
|
-
OpenInference
|
|
11
|
+
observability component can export as ATIF JSON and typed OpenTelemetry spans,
|
|
12
|
+
including the OpenInference projection for Phoenix. The same generic plugin config path can initialize
|
|
13
13
|
Adaptive components for hook-backed telemetry learning.
|
|
14
14
|
|
|
15
15
|
This public OpenClaw plugin package uses OpenClaw public hooks. It can run
|
|
@@ -33,8 +33,8 @@ through a public plugin hook.
|
|
|
33
33
|
- Generic NeMo Relay plugin initialization through `config.plugins`.
|
|
34
34
|
- ATIF JSON export through the built-in `observability` component.
|
|
35
35
|
- Adaptive plugin initialization through `config.plugins`.
|
|
36
|
-
- Optional OpenTelemetry OTLP
|
|
37
|
-
|
|
36
|
+
- Optional typed OpenTelemetry OTLP endpoints using `full`, `gen_ai`, or
|
|
37
|
+
`openinference` projection.
|
|
38
38
|
- Bounded LLM replay correlation across supported OpenClaw hooks.
|
|
39
39
|
- Tool span replay with conservative privacy defaults.
|
|
40
40
|
- Admin-scoped `nemoRelay.status` gateway health method.
|
|
@@ -82,7 +82,7 @@ OpenClaw plugin configuration under `plugins.entries["nemo-relay"].config`:
|
|
|
82
82
|
"kind": "observability",
|
|
83
83
|
"enabled": true,
|
|
84
84
|
"config": {
|
|
85
|
-
"version":
|
|
85
|
+
"version": 3,
|
|
86
86
|
"atif": {
|
|
87
87
|
"enabled": true,
|
|
88
88
|
"agent_name": "openclaw",
|
|
@@ -90,15 +90,20 @@ OpenClaw plugin configuration under `plugins.entries["nemo-relay"].config`:
|
|
|
90
90
|
},
|
|
91
91
|
"opentelemetry": {
|
|
92
92
|
"enabled": false,
|
|
93
|
-
"
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
93
|
+
"endpoints": [
|
|
94
|
+
{
|
|
95
|
+
"type": "full",
|
|
96
|
+
"transport": "http_binary",
|
|
97
|
+
"endpoint": "http://localhost:4318/v1/traces",
|
|
98
|
+
"service_name": "openclaw-nemo-relay"
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
"type": "openinference",
|
|
102
|
+
"transport": "http_binary",
|
|
103
|
+
"endpoint": "http://localhost:6006/v1/traces",
|
|
104
|
+
"service_name": "openclaw-nemo-relay"
|
|
105
|
+
}
|
|
106
|
+
]
|
|
102
107
|
}
|
|
103
108
|
}
|
|
104
109
|
},
|
|
@@ -156,11 +161,10 @@ do not use, or set their `enabled` fields to `false`.
|
|
|
156
161
|
`adaptive`.
|
|
157
162
|
- `config.plugins.components[].config.atif` writes ATIF trajectory JSON files.
|
|
158
163
|
Set `output_directory` to the directory where OpenClaw should write files.
|
|
159
|
-
- `config.plugins.components[].config.opentelemetry` sends
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
is `true`.
|
|
164
|
+
- `config.plugins.components[].config.opentelemetry.endpoints` sends typed OTLP
|
|
165
|
+
spans when the OpenTelemetry section is enabled. Use `full` for the complete
|
|
166
|
+
NeMo Relay projection, `gen_ai` for standardized GenAI conventions, or
|
|
167
|
+
`openinference` for Phoenix and other OpenInference-compatible collectors.
|
|
164
168
|
- `config.plugins.components[]` entries with `kind: "adaptive"` initialize the
|
|
165
169
|
Adaptive plugin. In hook-backed OpenClaw mode, adaptive telemetry can consume
|
|
166
170
|
replayed NeMo Relay events, while request-rewrite features such as adaptive
|
|
@@ -191,9 +195,9 @@ sink:
|
|
|
191
195
|
|
|
192
196
|
- ATIF: confirm JSON files appear in the configured
|
|
193
197
|
`config.plugins.components[].config.atif.output_directory`.
|
|
194
|
-
- OpenTelemetry: confirm spans arrive at
|
|
195
|
-
|
|
196
|
-
|
|
198
|
+
- Typed OpenTelemetry: confirm spans arrive at every configured OTLP endpoint;
|
|
199
|
+
for an `openinference` endpoint, confirm the projected spans appear in
|
|
200
|
+
Phoenix or another OpenInference-compatible backend.
|
|
197
201
|
|
|
198
202
|
The plugin also registers the `operator.admin` scoped gateway method
|
|
199
203
|
`nemoRelay.status`. If your CLI is already paired with admin-capable gateway
|
|
@@ -229,9 +233,9 @@ session emits the relevant LLM, message-write, and tool hooks.
|
|
|
229
233
|
|
|
230
234
|
If no export output appears, verify
|
|
231
235
|
`config.plugins.components[].config.atif.output_directory`,
|
|
232
|
-
|
|
233
|
-
`config.plugins.components[].config.
|
|
234
|
-
configured collector or output directory is reachable.
|
|
236
|
+
or each
|
|
237
|
+
`config.plugins.components[].config.opentelemetry.endpoints[].endpoint`, then
|
|
238
|
+
confirm the configured collector or output directory is reachable.
|
|
235
239
|
|
|
236
240
|
## Development
|
|
237
241
|
|
package/dist/index.js
CHANGED
|
@@ -12,7 +12,7 @@ import { registerNemoRelayPlugin } from './src/runtime-state.js';
|
|
|
12
12
|
const nemoRelayPluginEntry = definePluginEntry({
|
|
13
13
|
id: 'nemo-relay',
|
|
14
14
|
name: 'NeMo Relay Observability',
|
|
15
|
-
description: 'ATIF
|
|
15
|
+
description: 'ATIF and typed OpenTelemetry telemetry through NeMo Relay',
|
|
16
16
|
configSchema: nemoRelayConfigSchema,
|
|
17
17
|
register(api) {
|
|
18
18
|
registerNemoRelayPlugin(api);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "nemo-relay",
|
|
3
3
|
"name": "NeMo Relay Observability",
|
|
4
|
-
"description": "ATIF
|
|
4
|
+
"description": "ATIF and typed OpenTelemetry telemetry through NeMo Relay.",
|
|
5
5
|
"activation": {
|
|
6
6
|
"onStartup": true
|
|
7
7
|
},
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
},
|
|
26
26
|
"plugins": {
|
|
27
27
|
"$ref": "#/$defs/nemoRelayPluginConfig",
|
|
28
|
-
"description": "Generic NeMo Relay plugin configuration document. Use the observability component to configure ATIF
|
|
28
|
+
"description": "Generic NeMo Relay plugin configuration document. Use the observability component to configure ATIF and typed OpenTelemetry exporters."
|
|
29
29
|
},
|
|
30
30
|
"capture": {
|
|
31
31
|
"type": "object",
|
package/dist/src/config.js
CHANGED
|
@@ -119,7 +119,7 @@ function rejectRemovedFields(raw) {
|
|
|
119
119
|
throw new Error('atif was removed; configure plugins.components[].config.atif on the observability component');
|
|
120
120
|
}
|
|
121
121
|
if (raw.telemetry !== undefined) {
|
|
122
|
-
throw new Error('telemetry was removed; configure plugins.components[].config.opentelemetry
|
|
122
|
+
throw new Error('telemetry was removed; configure plugins.components[].config.opentelemetry.endpoints on the observability component');
|
|
123
123
|
}
|
|
124
124
|
}
|
|
125
125
|
/** Keep parser behavior aligned with the manifest's additionalProperties=false contract. */
|
package/dist/src/health.js
CHANGED
|
@@ -36,8 +36,13 @@ function configuredObservabilityOutputs(config) {
|
|
|
36
36
|
}
|
|
37
37
|
const componentConfig = asRecord(record.config);
|
|
38
38
|
outputs.atif ||= sectionEnabled(componentConfig?.atif);
|
|
39
|
-
|
|
40
|
-
|
|
39
|
+
const opentelemetry = asRecord(componentConfig?.opentelemetry);
|
|
40
|
+
const opentelemetryEnabled = sectionEnabled(opentelemetry);
|
|
41
|
+
outputs.otel ||= opentelemetryEnabled;
|
|
42
|
+
outputs.openInference ||=
|
|
43
|
+
opentelemetryEnabled &&
|
|
44
|
+
Array.isArray(opentelemetry?.endpoints) &&
|
|
45
|
+
opentelemetry.endpoints.some((endpoint) => asRecord(endpoint)?.type === 'openinference');
|
|
41
46
|
}
|
|
42
47
|
return outputs;
|
|
43
48
|
}
|
|
@@ -281,7 +281,7 @@ export class HookReplayBackend {
|
|
|
281
281
|
this.materializeDeferredSessionRoot(session);
|
|
282
282
|
drainSession(this.sessionManager(), session);
|
|
283
283
|
closeSessionRoot(this.sessionManager(), session, summary, session.finalOutput ?? summary, metadata);
|
|
284
|
-
this.flushSubscriberDelivery('session_close');
|
|
284
|
+
await this.flushSubscriberDelivery('session_close');
|
|
285
285
|
this.forgetPendingSubagentLineage(session);
|
|
286
286
|
deleteSession(this.stateValue, session);
|
|
287
287
|
}
|
|
@@ -316,9 +316,9 @@ export class HookReplayBackend {
|
|
|
316
316
|
}
|
|
317
317
|
}
|
|
318
318
|
/** Wait for native subscriber/exporter delivery after a replay closure boundary. */
|
|
319
|
-
flushSubscriberDelivery(label) {
|
|
319
|
+
async flushSubscriberDelivery(label) {
|
|
320
320
|
try {
|
|
321
|
-
this.nf.flushSubscribers?.();
|
|
321
|
+
await this.nf.flushSubscribers?.();
|
|
322
322
|
}
|
|
323
323
|
catch (error) {
|
|
324
324
|
this.logBoundedWarn(`flush-subscribers:${label}`, `nemo-relay subscriber flush failed: label=${label} error=${toMessage(error)}`);
|
package/index.ts
CHANGED
|
@@ -17,7 +17,7 @@ type NemoRelayPluginEntry = ReturnType<typeof definePluginEntry>;
|
|
|
17
17
|
const nemoRelayPluginEntry: NemoRelayPluginEntry = definePluginEntry({
|
|
18
18
|
id: 'nemo-relay',
|
|
19
19
|
name: 'NeMo Relay Observability',
|
|
20
|
-
description: 'ATIF
|
|
20
|
+
description: 'ATIF and typed OpenTelemetry telemetry through NeMo Relay',
|
|
21
21
|
configSchema: nemoRelayConfigSchema,
|
|
22
22
|
register(api: OpenClawPluginApi) {
|
|
23
23
|
registerNemoRelayPlugin(api);
|
package/openclaw.plugin.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "nemo-relay",
|
|
3
3
|
"name": "NeMo Relay Observability",
|
|
4
|
-
"description": "ATIF
|
|
4
|
+
"description": "ATIF and typed OpenTelemetry telemetry through NeMo Relay.",
|
|
5
5
|
"activation": {
|
|
6
6
|
"onStartup": true
|
|
7
7
|
},
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
},
|
|
26
26
|
"plugins": {
|
|
27
27
|
"$ref": "#/$defs/nemoRelayPluginConfig",
|
|
28
|
-
"description": "Generic NeMo Relay plugin configuration document. Use the observability component to configure ATIF
|
|
28
|
+
"description": "Generic NeMo Relay plugin configuration document. Use the observability component to configure ATIF and typed OpenTelemetry exporters."
|
|
29
29
|
},
|
|
30
30
|
"capture": {
|
|
31
31
|
"type": "object",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nemo-relay-openclaw",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0-rc.2",
|
|
4
4
|
"description": "NeMo Relay-authored observability plugin for OpenClaw.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
|
@@ -63,7 +63,7 @@
|
|
|
63
63
|
"openclaw": "^2026.7.1"
|
|
64
64
|
},
|
|
65
65
|
"dependencies": {
|
|
66
|
-
"nemo-relay-node": "0.
|
|
66
|
+
"nemo-relay-node": "0.7.0-rc.2"
|
|
67
67
|
},
|
|
68
68
|
"devDependencies": {
|
|
69
69
|
"@types/node": "^24.0.0",
|
package/src/config.ts
CHANGED
|
@@ -178,7 +178,7 @@ function rejectRemovedFields(raw: Record<string, unknown>): void {
|
|
|
178
178
|
}
|
|
179
179
|
if (raw.telemetry !== undefined) {
|
|
180
180
|
throw new Error(
|
|
181
|
-
'telemetry was removed; configure plugins.components[].config.opentelemetry
|
|
181
|
+
'telemetry was removed; configure plugins.components[].config.opentelemetry.endpoints on the observability component',
|
|
182
182
|
);
|
|
183
183
|
}
|
|
184
184
|
}
|
package/src/health.ts
CHANGED
|
@@ -85,8 +85,13 @@ function configuredObservabilityOutputs(config: NemoRelayHookBackendConfig): {
|
|
|
85
85
|
|
|
86
86
|
const componentConfig = asRecord(record.config);
|
|
87
87
|
outputs.atif ||= sectionEnabled(componentConfig?.atif);
|
|
88
|
-
|
|
89
|
-
|
|
88
|
+
const opentelemetry = asRecord(componentConfig?.opentelemetry);
|
|
89
|
+
const opentelemetryEnabled = sectionEnabled(opentelemetry);
|
|
90
|
+
outputs.otel ||= opentelemetryEnabled;
|
|
91
|
+
outputs.openInference ||=
|
|
92
|
+
opentelemetryEnabled &&
|
|
93
|
+
Array.isArray(opentelemetry?.endpoints) &&
|
|
94
|
+
opentelemetry.endpoints.some((endpoint) => asRecord(endpoint)?.type === 'openinference');
|
|
90
95
|
}
|
|
91
96
|
|
|
92
97
|
return outputs;
|
package/src/hooks-backend.ts
CHANGED
|
@@ -426,7 +426,7 @@ export class HookReplayBackend {
|
|
|
426
426
|
this.materializeDeferredSessionRoot(session);
|
|
427
427
|
drainSession(this.sessionManager(), session);
|
|
428
428
|
closeSessionRoot(this.sessionManager(), session, summary, session.finalOutput ?? summary, metadata);
|
|
429
|
-
this.flushSubscriberDelivery('session_close');
|
|
429
|
+
await this.flushSubscriberDelivery('session_close');
|
|
430
430
|
this.forgetPendingSubagentLineage(session);
|
|
431
431
|
deleteSession(this.stateValue, session);
|
|
432
432
|
}
|
|
@@ -466,9 +466,9 @@ export class HookReplayBackend {
|
|
|
466
466
|
}
|
|
467
467
|
|
|
468
468
|
/** Wait for native subscriber/exporter delivery after a replay closure boundary. */
|
|
469
|
-
private flushSubscriberDelivery(label: string): void {
|
|
469
|
+
private async flushSubscriberDelivery(label: string): Promise<void> {
|
|
470
470
|
try {
|
|
471
|
-
this.nf.flushSubscribers?.();
|
|
471
|
+
await this.nf.flushSubscribers?.();
|
|
472
472
|
} catch (error) {
|
|
473
473
|
this.logBoundedWarn(
|
|
474
474
|
`flush-subscribers:${label}`,
|