@openclaw/diagnostics-prometheus 2026.9.1 → 2026.9.3

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.
Files changed (2) hide show
  1. package/dist/index.js +125 -73
  2. package/package.json +4 -4
package/dist/index.js CHANGED
@@ -1,7 +1,8 @@
1
1
  import { isInternalDiagnosticEventMetadata, redactSensitiveText } from "./api.js";
2
2
  import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
3
- import { normalizeDiagnosticLane, normalizeDiagnosticValue } from "openclaw/plugin-sdk/diagnostic-runtime";
3
+ import { isDiagnosticsEnabled, normalizeDiagnosticLane, normalizeDiagnosticValue } from "openclaw/plugin-sdk/diagnostic-runtime";
4
4
  import { asNonNegativeFiniteNumber } from "openclaw/plugin-sdk/number-runtime";
5
+ import { getPluginRuntimeGatewayRequestScope } from "openclaw/plugin-sdk/plugin-runtime";
5
6
  import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
6
7
  //#region extensions/diagnostics-prometheus/src/service.ts
7
8
  const DURATION_BUCKETS_SECONDS = [
@@ -70,7 +71,9 @@ function seconds(ms) {
70
71
  return value === void 0 ? void 0 : value / 1e3;
71
72
  }
72
73
  function sortedLabels(labels) {
73
- return Object.entries(labels).toSorted(([left], [right]) => left.localeCompare(right));
74
+ const entries = Object.entries(labels);
75
+ entries.sort(([left], [right]) => left.localeCompare(right));
76
+ return entries;
74
77
  }
75
78
  function metricKey(name, labels) {
76
79
  return `${name}|${JSON.stringify(sortedLabels(labels))}`;
@@ -81,10 +84,13 @@ function escapeHelp(value) {
81
84
  function escapeLabelValue(value) {
82
85
  return value.replace(/\\/g, "\\\\").replace(/\n/g, "\\n").replace(/"/g, "\\\"");
83
86
  }
87
+ function formatLabelEntry([key, value]) {
88
+ return `${key}="${escapeLabelValue(value)}"`;
89
+ }
84
90
  function formatLabels(labels) {
85
91
  const entries = sortedLabels(labels);
86
92
  if (entries.length === 0) return "";
87
- return `{${entries.map(([key, value]) => `${key}="${escapeLabelValue(value)}"`).join(",")}}`;
93
+ return `{${entries.map(formatLabelEntry).join(",")}}`;
88
94
  }
89
95
  function formatPrometheusNumber(value) {
90
96
  if (!Number.isFinite(value)) return "0";
@@ -95,9 +101,8 @@ function createPrometheusMetricStore() {
95
101
  const gauges = /* @__PURE__ */ new Map();
96
102
  const histograms = /* @__PURE__ */ new Map();
97
103
  let droppedSeries = 0;
98
- const canCreateSeries = (map, key, metricName) => {
104
+ const canCreateSeries = (map, key) => {
99
105
  if (map.has(key)) return true;
100
- if (metricName === DROPPED_SERIES_COUNTER_NAME) return true;
101
106
  if (counters.size + gauges.size + histograms.size < MAX_PROMETHEUS_SERIES) return true;
102
107
  droppedSeries += 1;
103
108
  return false;
@@ -105,7 +110,7 @@ function createPrometheusMetricStore() {
105
110
  const counter = (name, help, labels, amount = 1) => {
106
111
  if (!Number.isFinite(amount) || amount <= 0) return;
107
112
  const key = metricKey(name, labels);
108
- if (!canCreateSeries(counters, key, name)) return;
113
+ if (!canCreateSeries(counters, key)) return;
109
114
  const existing = counters.get(key);
110
115
  if (existing) {
111
116
  existing.value += amount;
@@ -120,7 +125,7 @@ function createPrometheusMetricStore() {
120
125
  const gauge = (name, help, labels, value) => {
121
126
  if (value === void 0 || !Number.isFinite(value)) return;
122
127
  const key = metricKey(name, labels);
123
- if (!canCreateSeries(gauges, key, name)) return;
128
+ if (!canCreateSeries(gauges, key)) return;
124
129
  gauges.set(key, {
125
130
  help,
126
131
  labels,
@@ -130,7 +135,7 @@ function createPrometheusMetricStore() {
130
135
  const histogram = (name, help, labels, value, buckets = DURATION_BUCKETS_SECONDS) => {
131
136
  if (value === void 0 || !Number.isFinite(value) || value < 0) return;
132
137
  const key = metricKey(name, labels);
133
- if (!canCreateSeries(histograms, key, name)) return;
138
+ if (!canCreateSeries(histograms, key)) return;
134
139
  let sample = histograms.get(key);
135
140
  if (!sample) {
136
141
  sample = {
@@ -151,16 +156,16 @@ function createPrometheusMetricStore() {
151
156
  }
152
157
  };
153
158
  const snapshot = () => {
154
- const counterSnapshot = new Map(counters);
155
- if (droppedSeries > 0) counterSnapshot.set(metricKey(DROPPED_SERIES_COUNTER_NAME, {}), {
159
+ const counterSnapshot = [...counters];
160
+ if (droppedSeries > 0) counterSnapshot.push([metricKey(DROPPED_SERIES_COUNTER_NAME, {}), {
156
161
  help: "Prometheus metric series dropped because the exporter series cap was reached.",
157
162
  labels: {},
158
163
  value: droppedSeries
159
- });
164
+ }]);
160
165
  return {
161
166
  counters: counterSnapshot,
162
- gauges: new Map(gauges),
163
- histograms: new Map(histograms)
167
+ gauges: [...gauges],
168
+ histograms: [...histograms]
164
169
  };
165
170
  };
166
171
  const reset = () => {
@@ -194,36 +199,35 @@ function renderPrometheusMetrics(store) {
194
199
  lines.push(`# HELP ${name} ${escapeHelp(help)}`);
195
200
  lines.push(`# TYPE ${name} ${type}`);
196
201
  };
197
- const counterEntries = [...snapshot.counters.entries()].toSorted(([left], [right]) => left.localeCompare(right));
198
- for (const [key, sample] of counterEntries) {
199
- const name = key.split("|", 1)[0] ?? "";
200
- emitHeader(name, "counter", sample.help);
201
- lines.push(`${name}${formatLabels(sample.labels)} ${formatPrometheusNumber(sample.value)}`);
202
- }
203
- const gaugeEntries = [...snapshot.gauges.entries()].toSorted(([left], [right]) => left.localeCompare(right));
204
- for (const [key, sample] of gaugeEntries) {
205
- const name = key.split("|", 1)[0] ?? "";
206
- emitHeader(name, "gauge", sample.help);
207
- lines.push(`${name}${formatLabels(sample.labels)} ${formatPrometheusNumber(sample.value)}`);
202
+ for (const [type, entries] of [["counter", snapshot.counters], ["gauge", snapshot.gauges]]) {
203
+ entries.sort(([left], [right]) => left.localeCompare(right));
204
+ for (const [key, sample] of entries) {
205
+ const name = key.split("|", 1)[0] ?? "";
206
+ emitHeader(name, type, sample.help);
207
+ lines.push(`${name}${formatLabels(sample.labels)} ${formatPrometheusNumber(sample.value)}`);
208
+ }
208
209
  }
209
- const histogramEntries = [...snapshot.histograms.entries()].toSorted(([left], [right]) => left.localeCompare(right));
210
- for (const [key, sample] of histogramEntries) {
210
+ snapshot.histograms.sort(([left], [right]) => left.localeCompare(right));
211
+ for (const [key, sample] of snapshot.histograms) {
211
212
  const name = key.split("|", 1)[0] ?? "";
212
213
  emitHeader(name, "histogram", sample.help);
214
+ const labels = formatLabels(sample.labels);
215
+ const bucketLabels = sortedLabels({
216
+ ...sample.labels,
217
+ le: ""
218
+ });
219
+ const boundIndex = bucketLabels.findIndex(([labelKey]) => labelKey === "le");
220
+ const bucketFragments = bucketLabels.map(formatLabelEntry);
213
221
  for (let index = 0; index < sample.buckets.length; index += 1) {
214
222
  const bucket = sample.buckets[index];
215
223
  if (bucket === void 0) continue;
216
- lines.push(`${name}_bucket${formatLabels({
217
- ...sample.labels,
218
- le: String(bucket)
219
- })} ${formatPrometheusNumber(sample.counts[index] ?? 0)}`);
224
+ bucketFragments[boundIndex] = `le="${String(bucket)}"`;
225
+ lines.push(`${name}_bucket{${bucketFragments.join(",")}} ${formatPrometheusNumber(sample.counts[index] ?? 0)}`);
220
226
  }
221
- lines.push(`${name}_bucket${formatLabels({
222
- ...sample.labels,
223
- le: "+Inf"
224
- })} ${formatPrometheusNumber(sample.count)}`);
225
- lines.push(`${name}_sum${formatLabels(sample.labels)} ${formatPrometheusNumber(sample.sum)}`);
226
- lines.push(`${name}_count${formatLabels(sample.labels)} ${formatPrometheusNumber(sample.count)}`);
227
+ bucketFragments[boundIndex] = "le=\"+Inf\"";
228
+ lines.push(`${name}_bucket{${bucketFragments.join(",")}} ${formatPrometheusNumber(sample.count)}`);
229
+ lines.push(`${name}_sum${labels} ${formatPrometheusNumber(sample.sum)}`);
230
+ lines.push(`${name}_count${labels} ${formatPrometheusNumber(sample.count)}`);
227
231
  }
228
232
  lines.push("");
229
233
  return lines.join("\n");
@@ -373,6 +377,30 @@ function recordModelUsage(store, evt) {
373
377
  function recordDiagnosticEvent(store, evt, metadata) {
374
378
  if (!shouldRecordDiagnosticEvent(metadata)) return;
375
379
  switch (evt.type) {
380
+ case "diagnostic.gc":
381
+ store.histogram("openclaw_gc_duration_seconds", "Elapsed garbage collection duration in seconds for the hosting JavaScript isolate.", {}, seconds(evt.durationMs));
382
+ return;
383
+ case "gateway.event_loop.sample":
384
+ store.histogram("openclaw_gateway_event_loop_delay_max_seconds", "Maximum event-loop delay per completed Gateway observation window in seconds.", {}, seconds(evt.delayMaxMs));
385
+ store.counter("openclaw_gateway_event_loop_observed_seconds_total", "Elapsed seconds covered by completed Gateway event-loop observation windows.", {}, evt.intervalMs / 1e3);
386
+ return;
387
+ case "gateway.rpc": {
388
+ const labels = { method: evt.method };
389
+ if (evt.phase === "received") {
390
+ store.counter("openclaw_gateway_rpc_requests_total", "Authenticated Gateway WebSocket requests received.", labels);
391
+ return;
392
+ }
393
+ store.counter("openclaw_gateway_rpc_outcomes_total", "Gateway RPC observations by phase and outcome.", {
394
+ phase: evt.phase,
395
+ outcome: evt.outcome
396
+ });
397
+ if (evt.phase === "response" && (evt.outcome === "ok" || evt.outcome === "error")) store.histogram("openclaw_gateway_rpc_first_response_seconds", "Elapsed time until the first Gateway RPC response is sent.", labels, seconds(evt.durationMs));
398
+ else if (evt.phase === "handler") {
399
+ store.histogram("openclaw_gateway_rpc_handler_seconds", "Gateway RPC handler duration until return or throw.", labels, seconds(evt.durationMs));
400
+ store.histogram("openclaw_gateway_rpc_admission_seconds", "Elapsed time from Gateway RPC receipt until handler invocation.", labels, seconds(evt.admissionMs));
401
+ } else if (evt.phase === "dispatch") store.histogram("openclaw_gateway_rpc_queue_wait_seconds", "Gateway operator request start queue wait.", labels, seconds(evt.queueWaitMs));
402
+ return;
403
+ }
376
404
  case "model.usage":
377
405
  recordModelUsage(store, evt);
378
406
  return;
@@ -404,18 +432,16 @@ function recordDiagnosticEvent(store, evt, metadata) {
404
432
  store.histogram("openclaw_harness_run_duration_seconds", "Agent harness run duration in seconds.", harnessLabels(evt), seconds(evt.durationMs));
405
433
  store.counter("openclaw_harness_run_total", "Agent harness runs completed by outcome.", harnessLabels(evt));
406
434
  return;
407
- case "message.processed":
408
- store.counter("openclaw_message_processed_total", "Inbound messages processed by outcome.", {
435
+ case "message.processed": {
436
+ const labels = {
409
437
  channel: normalizeDiagnosticValue(evt.channel),
410
438
  outcome: evt.outcome,
411
439
  reason: normalizeDiagnosticValue(evt.reason, "none")
412
- });
413
- store.histogram("openclaw_message_processed_duration_seconds", "Inbound message processing duration in seconds.", {
414
- channel: normalizeDiagnosticValue(evt.channel),
415
- outcome: evt.outcome,
416
- reason: normalizeDiagnosticValue(evt.reason, "none")
417
- }, seconds(evt.durationMs));
440
+ };
441
+ store.counter("openclaw_message_processed_total", "Inbound messages processed by outcome.", labels);
442
+ store.histogram("openclaw_message_processed_duration_seconds", "Inbound message processing duration in seconds.", labels, seconds(evt.durationMs));
418
443
  return;
444
+ }
419
445
  case "webhook.received":
420
446
  store.counter("openclaw_webhook_received_total", "Webhook requests received by channel and update type.", webhookLabels(evt));
421
447
  return;
@@ -443,35 +469,29 @@ function recordDiagnosticEvent(store, evt, metadata) {
443
469
  source: normalizeDiagnosticValue(evt.source)
444
470
  });
445
471
  return;
446
- case "message.dispatch.completed":
447
- store.counter("openclaw_message_dispatch_completed_total", "Inbound message dispatch attempts completed by outcome.", {
448
- channel: normalizeDiagnosticValue(evt.channel),
449
- outcome: evt.outcome,
450
- reason: normalizeDiagnosticValue(evt.reason, "none"),
451
- source: normalizeDiagnosticValue(evt.source)
452
- });
453
- store.histogram("openclaw_message_dispatch_duration_seconds", "Inbound message dispatch duration in seconds.", {
472
+ case "message.dispatch.completed": {
473
+ const labels = {
454
474
  channel: normalizeDiagnosticValue(evt.channel),
455
475
  outcome: evt.outcome,
456
476
  reason: normalizeDiagnosticValue(evt.reason, "none"),
457
477
  source: normalizeDiagnosticValue(evt.source)
458
- }, seconds(evt.durationMs));
478
+ };
479
+ store.counter("openclaw_message_dispatch_completed_total", "Inbound message dispatch attempts completed by outcome.", labels);
480
+ store.histogram("openclaw_message_dispatch_duration_seconds", "Inbound message dispatch duration in seconds.", labels, seconds(evt.durationMs));
459
481
  return;
482
+ }
460
483
  case "message.delivery.completed":
461
- case "message.delivery.error":
462
- store.counter("openclaw_message_delivery_total", "Outbound message delivery attempts by outcome.", {
463
- channel: normalizeDiagnosticValue(evt.channel),
464
- delivery_kind: normalizeDiagnosticValue(evt.deliveryKind, "other"),
465
- error_category: evt.type === "message.delivery.error" ? normalizeDiagnosticValue(evt.errorCategory, "other") : "none",
466
- outcome: evt.type === "message.delivery.error" ? "error" : "completed"
467
- });
468
- store.histogram("openclaw_message_delivery_duration_seconds", "Outbound message delivery duration in seconds.", {
484
+ case "message.delivery.error": {
485
+ const labels = {
469
486
  channel: normalizeDiagnosticValue(evt.channel),
470
487
  delivery_kind: normalizeDiagnosticValue(evt.deliveryKind, "other"),
471
488
  error_category: evt.type === "message.delivery.error" ? normalizeDiagnosticValue(evt.errorCategory, "other") : "none",
472
489
  outcome: evt.type === "message.delivery.error" ? "error" : "completed"
473
- }, seconds(evt.durationMs));
490
+ };
491
+ store.counter("openclaw_message_delivery_total", "Outbound message delivery attempts by outcome.", labels);
492
+ store.histogram("openclaw_message_delivery_duration_seconds", "Outbound message delivery duration in seconds.", labels, seconds(evt.durationMs));
474
493
  return;
494
+ }
475
495
  case "talk.event":
476
496
  store.counter("openclaw_talk_event_total", "Talk events emitted by type.", talkLabels(evt));
477
497
  store.histogram("openclaw_talk_event_duration_seconds", "Talk event duration in seconds when reported.", talkLabels(evt), seconds(evt.durationMs));
@@ -506,9 +526,11 @@ function recordDiagnosticEvent(store, evt, metadata) {
506
526
  });
507
527
  return;
508
528
  case "diagnostic.memory.sample":
509
- store.gauge("openclaw_memory_bytes", "Latest process memory usage by memory kind.", { kind: "rss" }, evt.memory.rssBytes);
510
- store.gauge("openclaw_memory_bytes", "Latest process memory usage by memory kind.", { kind: "heap_total" }, evt.memory.heapTotalBytes);
511
- store.gauge("openclaw_memory_bytes", "Latest process memory usage by memory kind.", { kind: "heap_used" }, evt.memory.heapUsedBytes);
529
+ for (const [kind, field] of [
530
+ ["rss", "rssBytes"],
531
+ ["heap_total", "heapTotalBytes"],
532
+ ["heap_used", "heapUsedBytes"]
533
+ ]) store.gauge("openclaw_memory_bytes", "Latest process memory usage by memory kind.", { kind }, evt.memory[field]);
512
534
  store.histogram("openclaw_memory_rss_bytes", "RSS memory sample distribution in bytes.", {}, asNonNegativeFiniteNumber(evt.memory.rssBytes), BYTE_BUCKETS);
513
535
  return;
514
536
  case "diagnostic.memory.pressure":
@@ -519,19 +541,26 @@ function recordDiagnosticEvent(store, evt, metadata) {
519
541
  return;
520
542
  case "diagnostic.liveness.warning":
521
543
  store.counter("openclaw_liveness_warning_total", "Diagnostic liveness warning events.", livenessLabels(evt));
522
- store.gauge("openclaw_liveness_sessions", "Latest session counts reported with diagnostic liveness warnings.", { state: "active" }, asNonNegativeFiniteNumber(evt.active));
523
- store.gauge("openclaw_liveness_sessions", "Latest session counts reported with diagnostic liveness warnings.", { state: "waiting" }, asNonNegativeFiniteNumber(evt.waiting));
524
- store.gauge("openclaw_liveness_sessions", "Latest session counts reported with diagnostic liveness warnings.", { state: "queued" }, asNonNegativeFiniteNumber(evt.queued));
544
+ for (const state of [
545
+ "active",
546
+ "waiting",
547
+ "queued"
548
+ ]) store.gauge("openclaw_liveness_sessions", "Latest session counts reported with diagnostic liveness warnings.", { state }, asNonNegativeFiniteNumber(evt[state]));
525
549
  store.histogram("openclaw_liveness_event_loop_delay_p99_seconds", "P99 event-loop delay reported by diagnostic liveness warnings in seconds.", livenessLabels(evt), seconds(evt.eventLoopDelayP99Ms));
526
550
  store.histogram("openclaw_liveness_event_loop_delay_max_seconds", "Maximum event-loop delay reported by diagnostic liveness warnings in seconds.", livenessLabels(evt), seconds(evt.eventLoopDelayMaxMs));
527
551
  store.histogram("openclaw_liveness_event_loop_utilization_ratio", "Event-loop utilization reported by diagnostic liveness warnings.", livenessLabels(evt), asNonNegativeFiniteNumber(evt.eventLoopUtilization), RATIO_BUCKETS);
528
- store.histogram("openclaw_liveness_cpu_core_ratio", "CPU core ratio reported by diagnostic liveness warnings.", livenessLabels(evt), asNonNegativeFiniteNumber(evt.cpuCoreRatio), RATIO_BUCKETS);
552
+ store.histogram("openclaw_liveness_cpu_core_ratio", "Whole-process CPU usage in core equivalents, including worker and native threads; can exceed 1.", livenessLabels(evt), asNonNegativeFiniteNumber(evt.cpuCoreRatio), RATIO_BUCKETS);
529
553
  return;
530
554
  case "diagnostic.async_queue.dropped":
531
- store.counter("openclaw_diagnostic_async_queue_dropped_total", "Async diagnostic queue drops by dropped event class.", { drop_class: "total" }, asNonNegativeFiniteNumber(evt.droppedEvents));
532
- if (evt.droppedTrustedEvents !== void 0) store.counter("openclaw_diagnostic_async_queue_dropped_total", "Async diagnostic queue drops by dropped event class.", { drop_class: "trusted" }, asNonNegativeFiniteNumber(evt.droppedTrustedEvents));
533
- if (evt.droppedUntrustedEvents !== void 0) store.counter("openclaw_diagnostic_async_queue_dropped_total", "Async diagnostic queue drops by dropped event class.", { drop_class: "untrusted" }, asNonNegativeFiniteNumber(evt.droppedUntrustedEvents));
534
- if (evt.droppedPriorityEvents !== void 0) store.counter("openclaw_diagnostic_async_queue_dropped_total", "Async diagnostic queue drops by dropped event class.", { drop_class: "priority" }, asNonNegativeFiniteNumber(evt.droppedPriorityEvents));
555
+ for (const [dropClass, field] of [
556
+ ["total", "droppedEvents"],
557
+ ["trusted", "droppedTrustedEvents"],
558
+ ["untrusted", "droppedUntrustedEvents"],
559
+ ["priority", "droppedPriorityEvents"]
560
+ ]) {
561
+ if (field !== "droppedEvents" && evt[field] === void 0) continue;
562
+ store.counter("openclaw_diagnostic_async_queue_dropped_total", "Async diagnostic queue drops by dropped event class.", { drop_class: dropClass }, asNonNegativeFiniteNumber(evt[field]));
563
+ }
535
564
  store.gauge("openclaw_diagnostic_async_queue_length", "Latest async diagnostic queue length after a drop summary.", {}, asNonNegativeFiniteNumber(evt.queueLength));
536
565
  break;
537
566
  case "diagnostic.heartbeat": break;
@@ -548,8 +577,26 @@ function recordDiagnosticEvent(store, evt, metadata) {
548
577
  store.histogram("openclaw_payload_large_bytes", "Oversized payload byte sizes by surface and action.", payloadLargeLabels(evt), asNonNegativeFiniteNumber(evt.bytes), BYTE_BUCKETS);
549
578
  }
550
579
  }
580
+ const METRICS_READ_SCOPE = "operator.read";
581
+ const METRICS_READ_IMPLYING_SCOPES = [
582
+ METRICS_READ_SCOPE,
583
+ "operator.write",
584
+ "operator.admin"
585
+ ];
586
+ function hasMetricsReadScope() {
587
+ const runtimeScopes = getPluginRuntimeGatewayRequestScope()?.client?.connect?.scopes;
588
+ const scopes = Array.isArray(runtimeScopes) ? runtimeScopes : [];
589
+ return METRICS_READ_IMPLYING_SCOPES.some((scope) => scopes.includes(scope));
590
+ }
551
591
  function createMetricsHandler(store) {
552
592
  return (req, res) => {
593
+ if (!hasMetricsReadScope()) {
594
+ res.statusCode = 403;
595
+ res.setHeader("Cache-Control", "no-store");
596
+ res.setHeader("Content-Type", "text/plain; charset=utf-8");
597
+ res.end(`missing scope: ${METRICS_READ_SCOPE}`);
598
+ return true;
599
+ }
553
600
  if (req.method !== "GET" && req.method !== "HEAD") {
554
601
  res.statusCode = 405;
555
602
  res.setHeader("Allow", "GET, HEAD");
@@ -589,6 +636,11 @@ function createDiagnosticsPrometheusExporter() {
589
636
  ctx.logger.error("diagnostics-prometheus: internal diagnostics capability unavailable");
590
637
  return;
591
638
  }
639
+ const identity = isDiagnosticsEnabled(ctx.config) ? ctx.internalDiagnostics?.getRuntimeIdentity?.() : void 0;
640
+ if (identity) store.gauge("openclaw_gateway_build_info", "Identity of the hosting process and its loaded build; not a health or exporter epoch.", {
641
+ process_instance_id: identity.processInstanceId,
642
+ ...identity.buildId ? { build_id: identity.buildId } : {}
643
+ }, 1);
592
644
  unsubscribe = subscribe((event, metadata) => {
593
645
  try {
594
646
  recordDiagnosticEvent(store, event, metadata);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openclaw/diagnostics-prometheus",
3
- "version": "2026.9.1",
3
+ "version": "2026.9.3",
4
4
  "description": "OpenClaw diagnostics Prometheus exporter for runtime metrics.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -18,10 +18,10 @@
18
18
  "minHostVersion": ">=2026.4.25"
19
19
  },
20
20
  "compat": {
21
- "pluginApi": ">=2026.9.1"
21
+ "pluginApi": ">=2026.9.3"
22
22
  },
23
23
  "build": {
24
- "openclawVersion": "2026.9.1"
24
+ "openclawVersion": "2026.9.3"
25
25
  },
26
26
  "release": {
27
27
  "publishToClawHub": true,
@@ -37,7 +37,7 @@
37
37
  "README.md"
38
38
  ],
39
39
  "peerDependencies": {
40
- "openclaw": ">=2026.9.1"
40
+ "openclaw": ">=2026.9.3"
41
41
  },
42
42
  "peerDependenciesMeta": {
43
43
  "openclaw": {