@lumin-io/openclaw-diagnostics 0.1.0

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.
@@ -0,0 +1,48 @@
1
+ /**
2
+ * @lumin-io/openclaw-diagnostics — Lumin's deepest hook into OpenClaw.
3
+ *
4
+ * Two related but separate APIs in OpenClaw observe a model call:
5
+ *
6
+ * 1. `internalDiagnostics.onEvent` — the bus the bundled
7
+ * `@openclaw/diagnostics-otel` plugin reads. Carries timing,
8
+ * sizes, and provider IDs but the runtime never populates the
9
+ * `inputMessages` / `outputMessages` fields the OTel exporter
10
+ * tries to read, so the diagnostics-otel content-capture flag
11
+ * is effectively non-functional in 2026.5.x. Fixing that needs
12
+ * an upstream PR; meanwhile we route around it.
13
+ *
14
+ * 2. **Typed hooks** (`llm_input`, `llm_output`) — the registration
15
+ * surface used by trusted plugins. These DO carry full content
16
+ * (`prompt`, `historyMessages`, `systemPrompt`, `assistantTexts`,
17
+ * `usage`) at runtime, which is exactly what an observability
18
+ * tool needs.
19
+ *
20
+ * This plugin uses (2). On every llm_input / llm_output we build a
21
+ * Lumin SpanInput and POST to `/v1/spans`. The agent never blocks on
22
+ * us — failures are swallowed and budgeted with a short timeout.
23
+ *
24
+ * Activation prerequisite: non-bundled plugins must opt into
25
+ * conversation access, so the operator's openclaw.json must contain
26
+ *
27
+ * "plugins": {
28
+ * "entries": {
29
+ * "lumin-diagnostics": {
30
+ * "hooks": { "allowConversationAccess": true }
31
+ * }
32
+ * }
33
+ * }
34
+ *
35
+ * The plugin's install step writes that for the operator; if it's
36
+ * missing, OpenClaw silently drops the hook registration and we
37
+ * never see content. The plugin warns once at startup if the flag
38
+ * isn't set so the misconfiguration surfaces immediately.
39
+ */
40
+ declare const _default: {
41
+ id: string;
42
+ name: string;
43
+ description: string;
44
+ configSchema: import("openclaw/plugin-sdk/plugin-entry").OpenClawPluginConfigSchema;
45
+ register: NonNullable<import("openclaw/plugin-sdk/plugin-entry").OpenClawPluginDefinition["register"]>;
46
+ } & Pick<import("openclaw/plugin-sdk/plugin-entry").OpenClawPluginDefinition, "kind" | "reload" | "nodeHostCommands" | "securityAuditCollectors">;
47
+ export default _default;
48
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;;;;;;;;AAwWH,wBA2GG"}
package/dist/index.js ADDED
@@ -0,0 +1,384 @@
1
+ /**
2
+ * @lumin-io/openclaw-diagnostics — Lumin's deepest hook into OpenClaw.
3
+ *
4
+ * Two related but separate APIs in OpenClaw observe a model call:
5
+ *
6
+ * 1. `internalDiagnostics.onEvent` — the bus the bundled
7
+ * `@openclaw/diagnostics-otel` plugin reads. Carries timing,
8
+ * sizes, and provider IDs but the runtime never populates the
9
+ * `inputMessages` / `outputMessages` fields the OTel exporter
10
+ * tries to read, so the diagnostics-otel content-capture flag
11
+ * is effectively non-functional in 2026.5.x. Fixing that needs
12
+ * an upstream PR; meanwhile we route around it.
13
+ *
14
+ * 2. **Typed hooks** (`llm_input`, `llm_output`) — the registration
15
+ * surface used by trusted plugins. These DO carry full content
16
+ * (`prompt`, `historyMessages`, `systemPrompt`, `assistantTexts`,
17
+ * `usage`) at runtime, which is exactly what an observability
18
+ * tool needs.
19
+ *
20
+ * This plugin uses (2). On every llm_input / llm_output we build a
21
+ * Lumin SpanInput and POST to `/v1/spans`. The agent never blocks on
22
+ * us — failures are swallowed and budgeted with a short timeout.
23
+ *
24
+ * Activation prerequisite: non-bundled plugins must opt into
25
+ * conversation access, so the operator's openclaw.json must contain
26
+ *
27
+ * "plugins": {
28
+ * "entries": {
29
+ * "lumin-diagnostics": {
30
+ * "hooks": { "allowConversationAccess": true }
31
+ * }
32
+ * }
33
+ * }
34
+ *
35
+ * The plugin's install step writes that for the operator; if it's
36
+ * missing, OpenClaw silently drops the hook registration and we
37
+ * never see content. The plugin warns once at startup if the flag
38
+ * isn't set so the misconfiguration surfaces immediately.
39
+ */
40
+ import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
41
+ const DEFAULT_HOST = "http://localhost:8000";
42
+ const DEFAULT_PROJECT = "openclaw";
43
+ const DEFAULT_MAX_CONTENT_CHARS = 32_768;
44
+ const DEFAULT_TIMEOUT_MS = 5_000;
45
+ // ----- helpers ------------------------------------------------------------
46
+ function stringify(value, maxLen) {
47
+ if (value === null || value === undefined)
48
+ return undefined;
49
+ let s;
50
+ if (typeof value === "string") {
51
+ s = value;
52
+ }
53
+ else if (Array.isArray(value)) {
54
+ s = value
55
+ .map((v) => (typeof v === "string" ? v : safeJsonStringify(v)))
56
+ .join("\n");
57
+ }
58
+ else {
59
+ s = safeJsonStringify(value);
60
+ }
61
+ if (s.length <= maxLen)
62
+ return s;
63
+ return s.slice(0, maxLen - 16) + "…(truncated)";
64
+ }
65
+ function safeJsonStringify(v) {
66
+ try {
67
+ const out = JSON.stringify(v);
68
+ return out === undefined ? String(v) : out;
69
+ }
70
+ catch {
71
+ return String(v);
72
+ }
73
+ }
74
+ /**
75
+ * Format an OpenClaw 32-hex traceId / 16-hex spanId as a UUID so it
76
+ * collides with the trace_id schema Lumin already uses for OTLP-
77
+ * ingested spans. Also pads / hashes a fallback so we always get a
78
+ * deterministic 32-hex ID even when the hook runs without a trace
79
+ * scope.
80
+ */
81
+ function asUuid(hex, fallback) {
82
+ const h = (hex || "").toLowerCase().replace(/[^0-9a-f]/g, "");
83
+ const padded = h.length >= 32 ? h.slice(0, 32) : h.padStart(32, "0");
84
+ if (!padded || /^0+$/.test(padded)) {
85
+ const fp = fnv1a64(fallback).padStart(32, "0").slice(-32);
86
+ return uuidify(fp);
87
+ }
88
+ return uuidify(padded);
89
+ }
90
+ function uuidify(hex32) {
91
+ return `${hex32.slice(0, 8)}-${hex32.slice(8, 12)}-${hex32.slice(12, 16)}-${hex32.slice(16, 20)}-${hex32.slice(20, 32)}`;
92
+ }
93
+ function fnv1a64(s) {
94
+ let h = 0x811c9dc5;
95
+ for (let i = 0; i < s.length; i++) {
96
+ h ^= s.charCodeAt(i);
97
+ h = Math.imul(h, 0x01000193) >>> 0;
98
+ }
99
+ let h2 = 0xcbf29ce4;
100
+ for (let i = s.length - 1; i >= 0; i--) {
101
+ h2 ^= s.charCodeAt(i);
102
+ h2 = Math.imul(h2, 0x01000193) >>> 0;
103
+ }
104
+ return h.toString(16).padStart(8, "0") + h2.toString(16).padStart(8, "0");
105
+ }
106
+ function nowIso() {
107
+ return new Date().toISOString().replace("Z", "000Z");
108
+ }
109
+ /**
110
+ * Walk an OpenClaw assistant message's content blocks and concatenate
111
+ * any ``{type: "thinking", thinking: "..."}`` payloads. Reasoning
112
+ * models attach these BEFORE the visible text, but
113
+ * ``assistantTexts`` strips them. Returning undefined means "no
114
+ * thinking blocks present" — the caller should leave the metadata
115
+ * field absent rather than write an empty string.
116
+ *
117
+ * Defensive: lastAssistant is typed as ``unknown`` and provider
118
+ * shapes drift, so every step type-checks before recursing.
119
+ */
120
+ function extractThinkingFromAssistant(lastAssistant) {
121
+ if (!lastAssistant || typeof lastAssistant !== "object")
122
+ return undefined;
123
+ const content = lastAssistant.content;
124
+ if (!Array.isArray(content))
125
+ return undefined;
126
+ const parts = [];
127
+ for (const block of content) {
128
+ if (!block || typeof block !== "object")
129
+ continue;
130
+ const b = block;
131
+ if (b.type === "thinking" && typeof b.thinking === "string" && b.thinking.length > 0) {
132
+ parts.push(b.thinking);
133
+ }
134
+ }
135
+ return parts.length > 0 ? parts.join("\n\n") : undefined;
136
+ }
137
+ // ----- transport ----------------------------------------------------------
138
+ class LuminClient {
139
+ host;
140
+ project;
141
+ timeoutMs;
142
+ failureLogged = false;
143
+ constructor(cfg) {
144
+ this.host = (cfg.host || process.env.LUMIN_HOST || DEFAULT_HOST).replace(/\/+$/, "");
145
+ this.project = cfg.project || DEFAULT_PROJECT;
146
+ this.timeoutMs = cfg.timeoutMs ?? DEFAULT_TIMEOUT_MS;
147
+ }
148
+ /**
149
+ * Fire-and-forget POST. Rule 7 generalized: a Lumin outage must
150
+ * never affect the agent. The first failure logs once; subsequent
151
+ * failures are silenced.
152
+ */
153
+ async send(span) {
154
+ const body = JSON.stringify({ spans: [span] });
155
+ const ctrl = new AbortController();
156
+ const timer = setTimeout(() => ctrl.abort(), this.timeoutMs);
157
+ try {
158
+ const resp = await fetch(`${this.host}/v1/spans`, {
159
+ method: "POST",
160
+ headers: {
161
+ "Content-Type": "application/json",
162
+ "X-Lumin-Project": this.project,
163
+ },
164
+ body,
165
+ signal: ctrl.signal,
166
+ });
167
+ if (!resp.ok && !this.failureLogged) {
168
+ // eslint-disable-next-line no-console
169
+ console.warn(`[lumin-diagnostics] Lumin returned ${resp.status} ${resp.statusText} — further failures suppressed`);
170
+ this.failureLogged = true;
171
+ }
172
+ }
173
+ catch (err) {
174
+ if (!this.failureLogged) {
175
+ // eslint-disable-next-line no-console
176
+ console.warn(`[lumin-diagnostics] Could not reach Lumin at ${this.host}/v1/spans (${err.message}). Further failures suppressed.`);
177
+ this.failureLogged = true;
178
+ }
179
+ }
180
+ finally {
181
+ clearTimeout(timer);
182
+ }
183
+ }
184
+ }
185
+ const PENDING_TTL_MS = 5 * 60 * 1000;
186
+ class PendingLlmRegistry {
187
+ byRunId = new Map();
188
+ cleanupHandle;
189
+ set(runId, entry) {
190
+ this.byRunId.set(runId, entry);
191
+ this.scheduleSweep();
192
+ }
193
+ take(runId) {
194
+ const v = this.byRunId.get(runId);
195
+ if (v)
196
+ this.byRunId.delete(runId);
197
+ return v;
198
+ }
199
+ size() {
200
+ return this.byRunId.size;
201
+ }
202
+ scheduleSweep() {
203
+ if (this.cleanupHandle)
204
+ return;
205
+ this.cleanupHandle = setTimeout(() => {
206
+ this.cleanupHandle = undefined;
207
+ const now = Date.now();
208
+ for (const [k, v] of this.byRunId) {
209
+ if (now - v.startedAtMs > PENDING_TTL_MS)
210
+ this.byRunId.delete(k);
211
+ }
212
+ if (this.byRunId.size > 0)
213
+ this.scheduleSweep();
214
+ }, PENDING_TTL_MS);
215
+ // unref so a sweeper doesn't keep the gateway process alive
216
+ if (typeof this.cleanupHandle === "object" && this.cleanupHandle && "unref" in this.cleanupHandle) {
217
+ this.cleanupHandle.unref?.();
218
+ }
219
+ }
220
+ }
221
+ // ----- per-event translators ---------------------------------------------
222
+ function buildSpanFromPair(runId, pending, output, cfg, hookCtx) {
223
+ const maxLen = cfg.maxContentChars ?? DEFAULT_MAX_CONTENT_CHARS;
224
+ const trace = hookCtx?.trace || pending.trace;
225
+ const traceId = asUuid(trace?.traceId, runId);
226
+ const spanId = asUuid(trace?.spanId, `${runId}:llm`);
227
+ const parentId = trace?.parentSpanId
228
+ ? asUuid(trace.parentSpanId, runId)
229
+ : undefined;
230
+ const usage = output.usage || {};
231
+ const outputText = output.assistantTexts && output.assistantTexts.length
232
+ ? output.assistantTexts.join("\n")
233
+ : (output.lastAssistant !== undefined ? safeJsonStringify(output.lastAssistant) : undefined);
234
+ // Reasoning models (gpt-oss, claude-extended-thinking, o-series) emit
235
+ // ``{type: "thinking", thinking: "..."}`` blocks under
236
+ // ``lastAssistant.content`` BEFORE the visible text. ``assistantTexts``
237
+ // strips those out — fine for the operator-facing output, but we
238
+ // lose the reasoning trace entirely. Pull thinking out separately
239
+ // and surface it as a metadata field so operators can see WHY the
240
+ // model answered the way it did. Defensive parse: lastAssistant is
241
+ // typed as unknown by the public hook contract, so we check before
242
+ // walking it.
243
+ const thinkingText = extractThinkingFromAssistant(output.lastAssistant);
244
+ return {
245
+ id: spanId,
246
+ trace_id: traceId,
247
+ parent_span_id: parentId,
248
+ name: "openclaw.llm",
249
+ type: "llm",
250
+ started_at: pending.startedAt,
251
+ ended_at: nowIso(),
252
+ status: "ok",
253
+ model: output.model,
254
+ provider: output.provider,
255
+ tokens_input: usage.input,
256
+ tokens_output: usage.output,
257
+ input: pending.input,
258
+ output: outputText ? stringify(outputText, maxLen) : undefined,
259
+ session_id: output.sessionId || hookCtx?.sessionId,
260
+ metadata: {
261
+ "openclaw.runId": runId,
262
+ "openclaw.harnessId": output.harnessId,
263
+ "openclaw.resolvedRef": output.resolvedRef,
264
+ "openclaw.images_count": pending.imagesCount,
265
+ // Lightweight summary of what was replayed to the model, so an
266
+ // operator can see "this turn carried N prior messages and an
267
+ // M-character system prompt" without dragging the actual
268
+ // payload into the trace's input field.
269
+ "openclaw.history_message_count": pending.historyMessageCount,
270
+ "openclaw.system_prompt_chars": pending.systemPromptChars,
271
+ // Reasoning trace for models that emit thinking blocks. Always
272
+ // captured (no opt-in) because the whole point of an
273
+ // observability tool is to show WHY the agent answered the way
274
+ // it did — silently dropping the reasoning would defeat the
275
+ // purpose. Field is absent (not empty) when the model didn't
276
+ // emit thinking, so dashboard rendering can branch on
277
+ // presence rather than length.
278
+ ...(thinkingText !== undefined
279
+ ? {
280
+ "openclaw.content.thinking": stringify(thinkingText, maxLen),
281
+ "openclaw.thinking_chars": thinkingText.length,
282
+ }
283
+ : {}),
284
+ ...(cfg.captureSystemPrompt && pending.systemPrompt
285
+ ? { "openclaw.content.system_prompt": stringify(pending.systemPrompt, maxLen) }
286
+ : {}),
287
+ },
288
+ };
289
+ }
290
+ // ----- plugin entry -------------------------------------------------------
291
+ export default definePluginEntry({
292
+ id: "lumin-diagnostics",
293
+ name: "@lumin-io/openclaw-diagnostics",
294
+ description: "Streams full-fidelity OpenClaw runs (prompts, responses, tool I/O, tokens) to a local Lumin instance via /v1/spans.",
295
+ configSchema: {
296
+ type: "object",
297
+ properties: {
298
+ host: { type: "string" },
299
+ project: { type: "string" },
300
+ captureSystemPrompt: { type: "boolean" },
301
+ maxContentChars: { type: "number" },
302
+ timeoutMs: { type: "number" },
303
+ },
304
+ },
305
+ register: (api) => {
306
+ const apiAny = api;
307
+ const cfg = apiAny.pluginConfig || {};
308
+ const client = new LuminClient(cfg);
309
+ const pending = new PendingLlmRegistry();
310
+ const log = apiAny.logger;
311
+ if (typeof apiAny.on !== "function") {
312
+ // Older OpenClaw runtimes without typed-hook support won't
313
+ // expose `api.on`. Silently degrade: register nothing.
314
+ log?.warn?.("lumin-diagnostics: this OpenClaw build doesn't expose api.on for typed hooks; content capture disabled. Upgrade OpenClaw to >= 2026.5.x.");
315
+ return;
316
+ }
317
+ apiAny.on("llm_input", (rawEvent, rawCtx) => {
318
+ try {
319
+ const event = rawEvent;
320
+ const ctx = rawCtx;
321
+ const maxLen = cfg.maxContentChars ?? DEFAULT_MAX_CONTENT_CHARS;
322
+ // The input is JUST the user prompt for this turn. We
323
+ // deliberately do NOT pack history / systemPrompt into the
324
+ // input field: a Lumin trace represents one LLM call, not
325
+ // a conversation. Embedding the full chat history every
326
+ // turn (a) bloats every trace by 10x+ as conversations
327
+ // grow, (b) makes the dashboard view feel like a chat log
328
+ // instead of an agent run, and (c) is redundant with
329
+ // sessions, which group turns under the same conversation
330
+ // already.
331
+ //
332
+ // Counts and lightweight summaries go into metadata so
333
+ // operators can still see "this turn replayed 9 prior
334
+ // history messages" without the full payload.
335
+ const historyCount = Array.isArray(event.historyMessages)
336
+ ? event.historyMessages.length
337
+ : 0;
338
+ pending.set(event.runId, {
339
+ startedAt: nowIso(),
340
+ startedAtMs: Date.now(),
341
+ systemPrompt: event.systemPrompt,
342
+ systemPromptChars: typeof event.systemPrompt === "string"
343
+ ? event.systemPrompt.length
344
+ : 0,
345
+ historyMessageCount: historyCount,
346
+ input: stringify(event.prompt, maxLen),
347
+ imagesCount: event.imagesCount,
348
+ trace: ctx?.trace,
349
+ });
350
+ }
351
+ catch (err) {
352
+ log?.warn?.(`lumin-diagnostics: llm_input handler failed: ${err.message}`);
353
+ }
354
+ });
355
+ apiAny.on("llm_output", (rawEvent, rawCtx) => {
356
+ try {
357
+ const event = rawEvent;
358
+ const ctx = rawCtx;
359
+ const entry = pending.take(event.runId);
360
+ if (!entry) {
361
+ // No matching llm_input. Either the input hook dropped
362
+ // (e.g. raw model run path) or this is a fresh restart
363
+ // catching the tail of an in-flight run. Emit anyway —
364
+ // the operator still gets the assistant output.
365
+ const fallback = {
366
+ startedAt: nowIso(),
367
+ startedAtMs: Date.now(),
368
+ trace: ctx?.trace,
369
+ };
370
+ const span = buildSpanFromPair(event.runId, fallback, event, cfg, ctx);
371
+ void client.send(span).catch(() => { });
372
+ return;
373
+ }
374
+ const span = buildSpanFromPair(event.runId, entry, event, cfg, ctx);
375
+ void client.send(span).catch(() => { });
376
+ }
377
+ catch (err) {
378
+ log?.warn?.(`lumin-diagnostics: llm_output handler failed: ${err.message}`);
379
+ }
380
+ });
381
+ log?.info?.(`lumin-diagnostics: subscribed to llm_input + llm_output → ${cfg.host || process.env.LUMIN_HOST || DEFAULT_HOST}/v1/spans (project=${cfg.project || DEFAULT_PROJECT})`);
382
+ },
383
+ });
384
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AAEH,OAAO,EAAE,iBAAiB,EAAE,MAAM,kCAAkC,CAAC;AAYrE,MAAM,YAAY,GAAG,uBAAuB,CAAC;AAC7C,MAAM,eAAe,GAAG,UAAU,CAAC;AACnC,MAAM,yBAAyB,GAAG,MAAM,CAAC;AACzC,MAAM,kBAAkB,GAAG,KAAK,CAAC;AAsDjC,6EAA6E;AAE7E,SAAS,SAAS,CAAC,KAAc,EAAE,MAAc;IAC/C,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IAC5D,IAAI,CAAS,CAAC;IACd,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,CAAC,GAAG,KAAK,CAAC;IACZ,CAAC;SAAM,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QAChC,CAAC,GAAG,KAAK;aACN,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC,CAAC;aAC9D,IAAI,CAAC,IAAI,CAAC,CAAC;IAChB,CAAC;SAAM,CAAC;QACN,CAAC,GAAG,iBAAiB,CAAC,KAAK,CAAC,CAAC;IAC/B,CAAC;IACD,IAAI,CAAC,CAAC,MAAM,IAAI,MAAM;QAAE,OAAO,CAAC,CAAC;IACjC,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,MAAM,GAAG,EAAE,CAAC,GAAG,cAAc,CAAC;AAClD,CAAC;AAED,SAAS,iBAAiB,CAAC,CAAU;IACnC,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;QAC9B,OAAO,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;IAC7C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,MAAM,CAAC,CAAC,CAAC,CAAC;IACnB,CAAC;AACH,CAAC;AAED;;;;;;GAMG;AACH,SAAS,MAAM,CAAC,GAAuB,EAAE,QAAgB;IACvD,MAAM,CAAC,GAAG,CAAC,GAAG,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,YAAY,EAAE,EAAE,CAAC,CAAC;IAC9D,MAAM,MAAM,GAAG,CAAC,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;IACrE,IAAI,CAAC,MAAM,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;QACnC,MAAM,EAAE,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC,QAAQ,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC;QAC1D,OAAO,OAAO,CAAC,EAAE,CAAC,CAAC;IACrB,CAAC;IACD,OAAO,OAAO,CAAC,MAAM,CAAC,CAAC;AACzB,CAAC;AAED,SAAS,OAAO,CAAC,KAAa;IAC5B,OAAO,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,KAAK,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,CAAC,IAAI,KAAK,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,CAAC,IAAI,KAAK,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC;AAC3H,CAAC;AAED,SAAS,OAAO,CAAC,CAAS;IACxB,IAAI,CAAC,GAAG,UAAU,CAAC;IACnB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QAClC,CAAC,IAAI,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;QACrB,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,EAAE,UAAU,CAAC,KAAK,CAAC,CAAC;IACrC,CAAC;IACD,IAAI,EAAE,GAAG,UAAU,CAAC;IACpB,KAAK,IAAI,CAAC,GAAG,CAAC,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QACvC,EAAE,IAAI,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;QACtB,EAAE,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,UAAU,CAAC,KAAK,CAAC,CAAC;IACvC,CAAC;IACD,OAAO,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;AAC5E,CAAC;AAED,SAAS,MAAM;IACb,OAAO,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;AACvD,CAAC;AAGD;;;;;;;;;;GAUG;AACH,SAAS,4BAA4B,CAAC,aAAsB;IAC1D,IAAI,CAAC,aAAa,IAAI,OAAO,aAAa,KAAK,QAAQ;QAAE,OAAO,SAAS,CAAC;IAC1E,MAAM,OAAO,GAAI,aAAuC,CAAC,OAAO,CAAC;IACjE,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC;QAAE,OAAO,SAAS,CAAC;IAC9C,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ;YAAE,SAAS;QAClD,MAAM,CAAC,GAAG,KAA+D,CAAC;QAC1E,IAAI,CAAC,CAAC,IAAI,KAAK,UAAU,IAAI,OAAO,CAAC,CAAC,QAAQ,KAAK,QAAQ,IAAI,CAAC,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACrF,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;QACzB,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;AAC3D,CAAC;AAGD,6EAA6E;AAE7E,MAAM,WAAW;IACP,IAAI,CAAS;IACb,OAAO,CAAS;IAChB,SAAS,CAAS;IAClB,aAAa,GAAG,KAAK,CAAC;IAE9B,YAAY,GAA2B;QACrC,IAAI,CAAC,IAAI,GAAG,CAAC,GAAG,CAAC,IAAI,IAAI,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,YAAY,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;QACrF,IAAI,CAAC,OAAO,GAAG,GAAG,CAAC,OAAO,IAAI,eAAe,CAAC;QAC9C,IAAI,CAAC,SAAS,GAAG,GAAG,CAAC,SAAS,IAAI,kBAAkB,CAAC;IACvD,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,IAAI,CAAC,IAA6B;QACtC,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAC/C,MAAM,IAAI,GAAG,IAAI,eAAe,EAAE,CAAC;QACnC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,KAAK,EAAE,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;QAC7D,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,KAAK,CAAC,GAAG,IAAI,CAAC,IAAI,WAAW,EAAE;gBAChD,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE;oBACP,cAAc,EAAE,kBAAkB;oBAClC,iBAAiB,EAAE,IAAI,CAAC,OAAO;iBAChC;gBACD,IAAI;gBACJ,MAAM,EAAE,IAAI,CAAC,MAAM;aACpB,CAAC,CAAC;YACH,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,CAAC;gBACpC,sCAAsC;gBACtC,OAAO,CAAC,IAAI,CACV,sCAAsC,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,UAAU,gCAAgC,CACrG,CAAC;gBACF,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC;YAC5B,CAAC;QACH,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,CAAC;gBACxB,sCAAsC;gBACtC,OAAO,CAAC,IAAI,CACV,gDAAgD,IAAI,CAAC,IAAI,cAAe,GAAa,CAAC,OAAO,iCAAiC,CAC/H,CAAC;gBACF,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC;YAC5B,CAAC;QACH,CAAC;gBAAS,CAAC;YACT,YAAY,CAAC,KAAK,CAAC,CAAC;QACtB,CAAC;IACH,CAAC;CACF;AAqBD,MAAM,cAAc,GAAG,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC;AAErC,MAAM,kBAAkB;IACd,OAAO,GAAG,IAAI,GAAG,EAA0B,CAAC;IAC5C,aAAa,CAA4C;IAEjE,GAAG,CAAC,KAAa,EAAE,KAAqB;QACtC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;QAC/B,IAAI,CAAC,aAAa,EAAE,CAAC;IACvB,CAAC;IAED,IAAI,CAAC,KAAa;QAChB,MAAM,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QAClC,IAAI,CAAC;YAAE,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAClC,OAAO,CAAC,CAAC;IACX,CAAC;IAED,IAAI;QACF,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC;IAC3B,CAAC;IAEO,aAAa;QACnB,IAAI,IAAI,CAAC,aAAa;YAAE,OAAO;QAC/B,IAAI,CAAC,aAAa,GAAG,UAAU,CAAC,GAAG,EAAE;YACnC,IAAI,CAAC,aAAa,GAAG,SAAS,CAAC;YAC/B,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;YACvB,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;gBAClC,IAAI,GAAG,GAAG,CAAC,CAAC,WAAW,GAAG,cAAc;oBAAE,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;YACnE,CAAC;YACD,IAAI,IAAI,CAAC,OAAO,CAAC,IAAI,GAAG,CAAC;gBAAE,IAAI,CAAC,aAAa,EAAE,CAAC;QAClD,CAAC,EAAE,cAAc,CAAC,CAAC;QACnB,4DAA4D;QAC5D,IAAI,OAAO,IAAI,CAAC,aAAa,KAAK,QAAQ,IAAI,IAAI,CAAC,aAAa,IAAI,OAAO,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;YACjG,IAAI,CAAC,aAAwC,CAAC,KAAK,EAAE,EAAE,CAAC;QAC3D,CAAC;IACH,CAAC;CACF;AAGD,4EAA4E;AAE5E,SAAS,iBAAiB,CACxB,KAAa,EACb,OAAuB,EACvB,MAAsB,EACtB,GAA2B,EAC3B,OAAgC;IAEhC,MAAM,MAAM,GAAG,GAAG,CAAC,eAAe,IAAI,yBAAyB,CAAC;IAChE,MAAM,KAAK,GAAG,OAAO,EAAE,KAAK,IAAI,OAAO,CAAC,KAAK,CAAC;IAC9C,MAAM,OAAO,GAAG,MAAM,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC;IAC9C,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,KAAK,MAAM,CAAC,CAAC;IACrD,MAAM,QAAQ,GAAG,KAAK,EAAE,YAAY;QAClC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,YAAY,EAAE,KAAK,CAAC;QACnC,CAAC,CAAC,SAAS,CAAC;IAEd,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,IAAI,EAAE,CAAC;IACjC,MAAM,UAAU,GAAG,MAAM,CAAC,cAAc,IAAI,MAAM,CAAC,cAAc,CAAC,MAAM;QACtE,CAAC,CAAC,MAAM,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC;QAClC,CAAC,CAAC,CAAC,MAAM,CAAC,aAAa,KAAK,SAAS,CAAC,CAAC,CAAC,iBAAiB,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;IAE/F,sEAAsE;IACtE,uDAAuD;IACvD,wEAAwE;IACxE,iEAAiE;IACjE,kEAAkE;IAClE,kEAAkE;IAClE,mEAAmE;IACnE,mEAAmE;IACnE,cAAc;IACd,MAAM,YAAY,GAAG,4BAA4B,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC;IAExE,OAAO;QACL,EAAE,EAAE,MAAM;QACV,QAAQ,EAAE,OAAO;QACjB,cAAc,EAAE,QAAQ;QACxB,IAAI,EAAE,cAAc;QACpB,IAAI,EAAE,KAAK;QACX,UAAU,EAAE,OAAO,CAAC,SAAS;QAC7B,QAAQ,EAAE,MAAM,EAAE;QAClB,MAAM,EAAE,IAAI;QACZ,KAAK,EAAE,MAAM,CAAC,KAAK;QACnB,QAAQ,EAAE,MAAM,CAAC,QAAQ;QACzB,YAAY,EAAE,KAAK,CAAC,KAAK;QACzB,aAAa,EAAE,KAAK,CAAC,MAAM;QAC3B,KAAK,EAAE,OAAO,CAAC,KAAK;QACpB,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS;QAC9D,UAAU,EAAE,MAAM,CAAC,SAAS,IAAI,OAAO,EAAE,SAAS;QAClD,QAAQ,EAAE;YACR,gBAAgB,EAAE,KAAK;YACvB,oBAAoB,EAAE,MAAM,CAAC,SAAS;YACtC,sBAAsB,EAAE,MAAM,CAAC,WAAW;YAC1C,uBAAuB,EAAE,OAAO,CAAC,WAAW;YAC5C,+DAA+D;YAC/D,8DAA8D;YAC9D,yDAAyD;YACzD,wCAAwC;YACxC,gCAAgC,EAAE,OAAO,CAAC,mBAAmB;YAC7D,8BAA8B,EAAE,OAAO,CAAC,iBAAiB;YACzD,+DAA+D;YAC/D,qDAAqD;YACrD,+DAA+D;YAC/D,4DAA4D;YAC5D,6DAA6D;YAC7D,sDAAsD;YACtD,+BAA+B;YAC/B,GAAG,CAAC,YAAY,KAAK,SAAS;gBAC5B,CAAC,CAAC;oBACE,2BAA2B,EAAE,SAAS,CAAC,YAAY,EAAE,MAAM,CAAC;oBAC5D,yBAAyB,EAAE,YAAY,CAAC,MAAM;iBAC/C;gBACH,CAAC,CAAC,EAAE,CAAC;YACP,GAAG,CAAC,GAAG,CAAC,mBAAmB,IAAI,OAAO,CAAC,YAAY;gBACjD,CAAC,CAAC,EAAE,gCAAgC,EAAE,SAAS,CAAC,OAAO,CAAC,YAAY,EAAE,MAAM,CAAC,EAAE;gBAC/E,CAAC,CAAC,EAAE,CAAC;SACR;KACF,CAAC;AACJ,CAAC;AAGD,6EAA6E;AAE7E,eAAe,iBAAiB,CAAC;IAC/B,EAAE,EAAE,mBAAmB;IACvB,IAAI,EAAE,gCAAgC;IACtC,WAAW,EACT,qHAAqH;IACvH,YAAY,EAAE;QACZ,IAAI,EAAE,QAAQ;QACd,UAAU,EAAE;YACV,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;YACxB,OAAO,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;YAC3B,mBAAmB,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE;YACxC,eAAe,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;YACnC,SAAS,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;SAC9B;KACO;IACV,QAAQ,EAAE,CAAC,GAAG,EAAQ,EAAE;QACtB,MAAM,MAAM,GAAG,GAQd,CAAC;QACF,MAAM,GAAG,GAA2B,MAAM,CAAC,YAAY,IAAI,EAAE,CAAC;QAC9D,MAAM,MAAM,GAAG,IAAI,WAAW,CAAC,GAAG,CAAC,CAAC;QACpC,MAAM,OAAO,GAAG,IAAI,kBAAkB,EAAE,CAAC;QACzC,MAAM,GAAG,GAAG,MAAM,CAAC,MAAM,CAAC;QAE1B,IAAI,OAAO,MAAM,CAAC,EAAE,KAAK,UAAU,EAAE,CAAC;YACpC,2DAA2D;YAC3D,uDAAuD;YACvD,GAAG,EAAE,IAAI,EAAE,CACT,0IAA0I,CAC3I,CAAC;YACF,OAAO;QACT,CAAC;QAED,MAAM,CAAC,EAAE,CAAC,WAAW,EAAE,CAAC,QAAiB,EAAE,MAAe,EAAE,EAAE;YAC5D,IAAI,CAAC;gBACH,MAAM,KAAK,GAAG,QAAyB,CAAC;gBACxC,MAAM,GAAG,GAAG,MAAiC,CAAC;gBAC9C,MAAM,MAAM,GAAG,GAAG,CAAC,eAAe,IAAI,yBAAyB,CAAC;gBAChE,sDAAsD;gBACtD,2DAA2D;gBAC3D,0DAA0D;gBAC1D,wDAAwD;gBACxD,uDAAuD;gBACvD,0DAA0D;gBAC1D,qDAAqD;gBACrD,0DAA0D;gBAC1D,WAAW;gBACX,EAAE;gBACF,uDAAuD;gBACvD,sDAAsD;gBACtD,8CAA8C;gBAC9C,MAAM,YAAY,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,eAAe,CAAC;oBACvD,CAAC,CAAC,KAAK,CAAC,eAAe,CAAC,MAAM;oBAC9B,CAAC,CAAC,CAAC,CAAC;gBACN,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,EAAE;oBACvB,SAAS,EAAE,MAAM,EAAE;oBACnB,WAAW,EAAE,IAAI,CAAC,GAAG,EAAE;oBACvB,YAAY,EAAE,KAAK,CAAC,YAAY;oBAChC,iBAAiB,EAAE,OAAO,KAAK,CAAC,YAAY,KAAK,QAAQ;wBACvD,CAAC,CAAC,KAAK,CAAC,YAAY,CAAC,MAAM;wBAC3B,CAAC,CAAC,CAAC;oBACL,mBAAmB,EAAE,YAAY;oBACjC,KAAK,EAAE,SAAS,CAAC,KAAK,CAAC,MAAM,EAAE,MAAM,CAAC;oBACtC,WAAW,EAAE,KAAK,CAAC,WAAW;oBAC9B,KAAK,EAAE,GAAG,EAAE,KAAK;iBAClB,CAAC,CAAC;YACL,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,GAAG,EAAE,IAAI,EAAE,CAAC,gDAAiD,GAAa,CAAC,OAAO,EAAE,CAAC,CAAC;YACxF,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,MAAM,CAAC,EAAE,CAAC,YAAY,EAAE,CAAC,QAAiB,EAAE,MAAe,EAAE,EAAE;YAC7D,IAAI,CAAC;gBACH,MAAM,KAAK,GAAG,QAA0B,CAAC;gBACzC,MAAM,GAAG,GAAG,MAAiC,CAAC;gBAC9C,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;gBACxC,IAAI,CAAC,KAAK,EAAE,CAAC;oBACX,uDAAuD;oBACvD,uDAAuD;oBACvD,uDAAuD;oBACvD,gDAAgD;oBAChD,MAAM,QAAQ,GAAmB;wBAC/B,SAAS,EAAE,MAAM,EAAE;wBACnB,WAAW,EAAE,IAAI,CAAC,GAAG,EAAE;wBACvB,KAAK,EAAE,GAAG,EAAE,KAAK;qBAClB,CAAC;oBACF,MAAM,IAAI,GAAG,iBAAiB,CAAC,KAAK,CAAC,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;oBACvE,KAAK,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;oBACvC,OAAO;gBACT,CAAC;gBACD,MAAM,IAAI,GAAG,iBAAiB,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;gBACpE,KAAK,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;YACzC,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,GAAG,EAAE,IAAI,EAAE,CAAC,iDAAkD,GAAa,CAAC,OAAO,EAAE,CAAC,CAAC;YACzF,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,GAAG,EAAE,IAAI,EAAE,CACT,6DAA6D,GAAG,CAAC,IAAI,IAAI,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,YAAY,sBAAsB,GAAG,CAAC,OAAO,IAAI,eAAe,GAAG,CACvK,CAAC;IACJ,CAAC;CACF,CAAC,CAAC"}
@@ -0,0 +1,32 @@
1
+ {
2
+ "id": "lumin-diagnostics",
3
+ "activation": {
4
+ "onStartup": true
5
+ },
6
+ "configSchema": {
7
+ "type": "object",
8
+ "additionalProperties": false,
9
+ "properties": {
10
+ "host": {
11
+ "type": "string",
12
+ "description": "Lumin API base URL. Defaults to LUMIN_HOST env or http://localhost:8000."
13
+ },
14
+ "project": {
15
+ "type": "string",
16
+ "description": "Lumin project label sent as X-Lumin-Project. Defaults to 'openclaw'."
17
+ },
18
+ "captureSystemPrompt": {
19
+ "type": "boolean",
20
+ "description": "Whether to capture the system prompt text on each model.call. Default false (system prompts are usually large and rarely actionable)."
21
+ },
22
+ "maxContentChars": {
23
+ "type": "number",
24
+ "description": "Cap on per-attribute content size in characters. Defaults to 32768. Truncated values are tagged '...(truncated)'."
25
+ },
26
+ "timeoutMs": {
27
+ "type": "number",
28
+ "description": "HTTP timeout for POST /v1/spans, milliseconds. Default 5000."
29
+ }
30
+ }
31
+ }
32
+ }
package/package.json ADDED
@@ -0,0 +1,45 @@
1
+ {
2
+ "name": "@lumin-io/openclaw-diagnostics",
3
+ "version": "0.1.0",
4
+ "description": "Full-fidelity Lumin observability for OpenClaw — captures prompts, responses, tool I/O via OpenClaw's DiagnosticEvent bus (no privacy filter).",
5
+ "license": "Apache-2.0",
6
+ "type": "module",
7
+ "main": "dist/index.js",
8
+ "types": "dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "import": "./dist/index.js",
12
+ "types": "./dist/index.d.ts"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist",
17
+ "openclaw.plugin.json",
18
+ "README.md"
19
+ ],
20
+ "scripts": {
21
+ "build": "tsc",
22
+ "test": "vitest run"
23
+ },
24
+ "peerDependencies": {
25
+ "openclaw": ">=2026.4.25"
26
+ },
27
+ "devDependencies": {
28
+ "@types/node": "^20.0.0",
29
+ "typescript": "^5.3.0",
30
+ "vitest": "^2.1.9"
31
+ },
32
+ "openclaw": {
33
+ "extensions": [
34
+ "./dist/index.js"
35
+ ],
36
+ "install": {
37
+ "npmSpec": "@lumin-io/openclaw-diagnostics",
38
+ "defaultChoice": "npm",
39
+ "minHostVersion": ">=2026.4.25"
40
+ },
41
+ "compat": {
42
+ "pluginApi": ">=2026.5.4"
43
+ }
44
+ }
45
+ }