feat: add diagnostics events and otel exporter

This commit is contained in:
Peter Steinberger
2026-01-20 18:56:10 +00:00
parent b1f086b536
commit 5c4079f66c
14 changed files with 1030 additions and 13 deletions

View File

@@ -136,6 +136,72 @@ Tool summaries can redact sensitive tokens before they hit the console:
Redaction affects **console output only** and does not alter file logs.
## Diagnostics + OpenTelemetry
Diagnostics are **opt-in** structured events for model runs (usage + cost +
context + duration). They do **not** replace logs; they exist to feed metrics,
traces, and other exporters.
Clawdbot currently emits a `model.usage` event after each agent run with:
- Token counts (input/output/cache/prompt/total)
- Estimated cost (USD)
- Context window used/limit
- Duration (ms)
- Provider/channel/model + session identifiers
### Enable diagnostics (no exporter)
Use this if you want diagnostics events available to plugins or custom sinks:
```json
{
"diagnostics": {
"enabled": true
}
}
```
### Export to OpenTelemetry
Diagnostics can be exported via the `diagnostics-otel` plugin (OTLP/HTTP). This
works with any OpenTelemetry collector/backend that accepts OTLP/HTTP.
```json
{
"plugins": {
"allow": ["diagnostics-otel"],
"entries": {
"diagnostics-otel": {
"enabled": true
}
}
},
"diagnostics": {
"enabled": true,
"otel": {
"enabled": true,
"endpoint": "http://otel-collector:4318",
"protocol": "http/protobuf",
"serviceName": "clawdbot-gateway",
"traces": true,
"metrics": true,
"sampleRate": 0.2,
"flushIntervalMs": 60000
}
}
}
```
Notes:
- You can also enable the plugin with `clawdbot plugins enable diagnostics-otel`.
- `protocol` currently supports `http/protobuf`.
- Metrics include token usage, cost, context size, and run duration.
- Traces/metrics can be toggled with `traces` / `metrics` (default: on).
- Set `headers` when your collector requires auth.
- Environment variables supported: `OTEL_EXPORTER_OTLP_ENDPOINT`,
`OTEL_SERVICE_NAME`, `OTEL_EXPORTER_OTLP_PROTOCOL`.
## Troubleshooting tips
- **Gateway not reachable?** Run `clawdbot doctor` first.

View File

@@ -0,0 +1,8 @@
{
"id": "diagnostics-otel",
"configSchema": {
"type": "object",
"additionalProperties": false,
"properties": {}
}
}

View File

@@ -0,0 +1,16 @@
import type { ClawdbotPluginApi } from "clawdbot/plugin-sdk";
import { emptyPluginConfigSchema } from "clawdbot/plugin-sdk";
import { createDiagnosticsOtelService } from "./src/service.js";
const plugin = {
id: "diagnostics-otel",
name: "Diagnostics OpenTelemetry",
description: "Export diagnostics events to OpenTelemetry",
configSchema: emptyPluginConfigSchema(),
register(api: ClawdbotPluginApi) {
api.registerService(createDiagnosticsOtelService());
},
};
export default plugin;

View File

@@ -0,0 +1,19 @@
{
"name": "@clawdbot/diagnostics-otel",
"version": "2026.1.17-1",
"type": "module",
"description": "Clawdbot diagnostics OpenTelemetry exporter",
"clawdbot": {
"extensions": ["./index.ts"]
},
"dependencies": {
"@opentelemetry/api": "^1.9.0",
"@opentelemetry/exporter-metrics-otlp-http": "^0.210.0",
"@opentelemetry/exporter-trace-otlp-http": "^0.210.0",
"@opentelemetry/resources": "^2.4.0",
"@opentelemetry/sdk-metrics": "^2.4.0",
"@opentelemetry/sdk-node": "^0.210.0",
"@opentelemetry/sdk-trace-base": "^2.4.0",
"@opentelemetry/semantic-conventions": "^1.39.0"
}
}

View File

@@ -0,0 +1,196 @@
import { metrics, trace } from "@opentelemetry/api";
import { OTLPMetricExporter } from "@opentelemetry/exporter-metrics-otlp-http";
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
import { Resource } from "@opentelemetry/resources";
import { PeriodicExportingMetricReader } from "@opentelemetry/sdk-metrics";
import { NodeSDK } from "@opentelemetry/sdk-node";
import { ParentBasedSampler, TraceIdRatioBasedSampler } from "@opentelemetry/sdk-trace-base";
import { SemanticResourceAttributes } from "@opentelemetry/semantic-conventions";
import type {
ClawdbotPluginService,
DiagnosticUsageEvent,
} from "clawdbot/plugin-sdk";
import { onDiagnosticEvent } from "clawdbot/plugin-sdk";
const DEFAULT_SERVICE_NAME = "clawdbot";
function normalizeEndpoint(endpoint?: string): string | undefined {
const trimmed = endpoint?.trim();
return trimmed ? trimmed.replace(/\/+$/, "") : undefined;
}
function resolveOtelUrl(endpoint: string | undefined, path: string): string | undefined {
if (!endpoint) return undefined;
if (endpoint.includes("/v1/")) return endpoint;
return `${endpoint}/${path}`;
}
function resolveSampleRate(value: number | undefined): number | undefined {
if (typeof value !== "number" || !Number.isFinite(value)) return undefined;
if (value < 0 || value > 1) return undefined;
return value;
}
export function createDiagnosticsOtelService(): ClawdbotPluginService {
let sdk: NodeSDK | null = null;
let unsubscribe: (() => void) | null = null;
return {
id: "diagnostics-otel",
async start(ctx) {
const cfg = ctx.config.diagnostics;
const otel = cfg?.otel;
if (!cfg?.enabled || !otel?.enabled) return;
const protocol = otel.protocol ?? process.env.OTEL_EXPORTER_OTLP_PROTOCOL ?? "http/protobuf";
if (protocol !== "http/protobuf") {
ctx.logger.warn(`diagnostics-otel: unsupported protocol ${protocol}`);
return;
}
const endpoint = normalizeEndpoint(otel.endpoint ?? process.env.OTEL_EXPORTER_OTLP_ENDPOINT);
const headers = otel.headers ?? undefined;
const serviceName =
otel.serviceName?.trim() || process.env.OTEL_SERVICE_NAME || DEFAULT_SERVICE_NAME;
const sampleRate = resolveSampleRate(otel.sampleRate);
const tracesEnabled = otel.traces !== false;
const metricsEnabled = otel.metrics !== false;
if (!tracesEnabled && !metricsEnabled) return;
const resource = new Resource({
[SemanticResourceAttributes.SERVICE_NAME]: serviceName,
});
const traceUrl = resolveOtelUrl(endpoint, "v1/traces");
const metricUrl = resolveOtelUrl(endpoint, "v1/metrics");
const traceExporter = tracesEnabled
? new OTLPTraceExporter({
...(traceUrl ? { url: traceUrl } : {}),
...(headers ? { headers } : {}),
})
: undefined;
const metricExporter = metricsEnabled
? new OTLPMetricExporter({
...(metricUrl ? { url: metricUrl } : {}),
...(headers ? { headers } : {}),
})
: undefined;
const metricReader = metricExporter
? new PeriodicExportingMetricReader({
exporter: metricExporter,
...(typeof otel.flushIntervalMs === "number"
? { exportIntervalMillis: Math.max(1000, otel.flushIntervalMs) }
: {}),
})
: undefined;
sdk = new NodeSDK({
resource,
...(traceExporter ? { traceExporter } : {}),
...(metricReader ? { metricReader } : {}),
...(sampleRate !== undefined
? {
sampler: new ParentBasedSampler({
root: new TraceIdRatioBasedSampler(sampleRate),
}),
}
: {}),
});
await sdk.start();
const meter = metrics.getMeter("clawdbot");
const tracer = trace.getTracer("clawdbot");
const tokensCounter = meter.createCounter("clawdbot.tokens", {
unit: "1",
description: "Token usage by type",
});
const costCounter = meter.createCounter("clawdbot.cost.usd", {
unit: "1",
description: "Estimated model cost (USD)",
});
const durationHistogram = meter.createHistogram("clawdbot.run.duration_ms", {
unit: "ms",
description: "Agent run duration",
});
const contextHistogram = meter.createHistogram("clawdbot.context.tokens", {
unit: "1",
description: "Context window size and usage",
});
unsubscribe = onDiagnosticEvent((evt) => {
if (evt.type !== "model.usage") return;
const usageEvent = evt as DiagnosticUsageEvent;
const attrs = {
"clawdbot.channel": usageEvent.channel ?? "unknown",
"clawdbot.provider": usageEvent.provider ?? "unknown",
"clawdbot.model": usageEvent.model ?? "unknown",
};
const usage = usageEvent.usage;
if (usage.input) tokensCounter.add(usage.input, { ...attrs, "clawdbot.token": "input" });
if (usage.output)
tokensCounter.add(usage.output, { ...attrs, "clawdbot.token": "output" });
if (usage.cacheRead)
tokensCounter.add(usage.cacheRead, { ...attrs, "clawdbot.token": "cache_read" });
if (usage.cacheWrite)
tokensCounter.add(usage.cacheWrite, { ...attrs, "clawdbot.token": "cache_write" });
if (usage.promptTokens)
tokensCounter.add(usage.promptTokens, { ...attrs, "clawdbot.token": "prompt" });
if (usage.total)
tokensCounter.add(usage.total, { ...attrs, "clawdbot.token": "total" });
if (usageEvent.costUsd) costCounter.add(usageEvent.costUsd, attrs);
if (usageEvent.durationMs) durationHistogram.record(usageEvent.durationMs, attrs);
if (usageEvent.context?.limit)
contextHistogram.record(usageEvent.context.limit, {
...attrs,
"clawdbot.context": "limit",
});
if (usageEvent.context?.used)
contextHistogram.record(usageEvent.context.used, {
...attrs,
"clawdbot.context": "used",
});
if (!tracesEnabled) return;
const spanAttrs: Record<string, string | number> = {
...attrs,
"clawdbot.sessionKey": usageEvent.sessionKey ?? "",
"clawdbot.sessionId": usageEvent.sessionId ?? "",
"clawdbot.tokens.input": usage.input ?? 0,
"clawdbot.tokens.output": usage.output ?? 0,
"clawdbot.tokens.cache_read": usage.cacheRead ?? 0,
"clawdbot.tokens.cache_write": usage.cacheWrite ?? 0,
"clawdbot.tokens.total": usage.total ?? 0,
};
const startTime = usageEvent.durationMs
? Date.now() - Math.max(0, usageEvent.durationMs)
: undefined;
const span = tracer.startSpan("clawdbot.model.usage", {
attributes: spanAttrs,
...(startTime ? { startTime } : {}),
});
span.end();
});
if (otel.logs) {
ctx.logger.warn("diagnostics-otel: logs exporter not wired yet");
}
},
async stop() {
unsubscribe?.();
unsubscribe = null;
if (sdk) {
await sdk.shutdown().catch(() => undefined);
sdk = null;
}
},
} satisfies ClawdbotPluginService;
}

540
pnpm-lock.yaml generated
View File

@@ -243,7 +243,7 @@ importers:
version: 5.9.3
vitest:
specifier: ^4.0.17
version: 4.0.17(@types/node@25.0.9)(@vitest/browser-playwright@4.0.17)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2)
version: 4.0.17(@opentelemetry/api@1.9.0)(@types/node@25.0.9)(@vitest/browser-playwright@4.0.17)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2)
wireit:
specifier: ^0.14.12
version: 0.14.12
@@ -259,6 +259,33 @@ importers:
extensions/copilot-proxy: {}
extensions/diagnostics-otel:
dependencies:
'@opentelemetry/api':
specifier: ^1.9.0
version: 1.9.0
'@opentelemetry/exporter-metrics-otlp-http':
specifier: ^0.210.0
version: 0.210.0(@opentelemetry/api@1.9.0)
'@opentelemetry/exporter-trace-otlp-http':
specifier: ^0.210.0
version: 0.210.0(@opentelemetry/api@1.9.0)
'@opentelemetry/resources':
specifier: ^2.4.0
version: 2.4.0(@opentelemetry/api@1.9.0)
'@opentelemetry/sdk-metrics':
specifier: ^2.4.0
version: 2.4.0(@opentelemetry/api@1.9.0)
'@opentelemetry/sdk-node':
specifier: ^0.210.0
version: 0.210.0(@opentelemetry/api@1.9.0)
'@opentelemetry/sdk-trace-base':
specifier: ^2.4.0
version: 2.4.0(@opentelemetry/api@1.9.0)
'@opentelemetry/semantic-conventions':
specifier: ^1.39.0
version: 1.39.0
extensions/discord: {}
extensions/google-antigravity-auth: {}
@@ -393,7 +420,7 @@ importers:
version: 5.9.3
vitest:
specifier: 4.0.17
version: 4.0.17(@types/node@25.0.9)(@vitest/browser-playwright@4.0.17)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2)
version: 4.0.17(@opentelemetry/api@1.9.0)(@types/node@25.0.9)(@vitest/browser-playwright@4.0.17)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2)
packages:
@@ -817,6 +844,15 @@ packages:
'@grammyjs/types@3.23.0':
resolution: {integrity: sha512-D3jQ4UWERPsyR3op/YFudMMIPNTU47vy7L51uO9/73tMELmjO/+LX5N36/Y0CG5IQfIsz43MxiHI5rgsK0/k+g==}
'@grpc/grpc-js@1.14.3':
resolution: {integrity: sha512-Iq8QQQ/7X3Sac15oB6p0FmUg/klxQvXLeileoqrTRGJYLV+/9tubbr9ipz0GKHjmXVsgFPo/+W+2cA8eNcR+XA==}
engines: {node: '>=12.10.0'}
'@grpc/proto-loader@0.8.0':
resolution: {integrity: sha512-rc1hOQtjIWGxcxpb9aHAfLpIctjEnsDehj0DAiVfBlmT84uvR0uUtN2hEi/ecvWVjXUGf5qPF4qEgiLOx1YIMQ==}
engines: {node: '>=6'}
hasBin: true
'@hapi/boom@9.1.4':
resolution: {integrity: sha512-Ls1oH8jaN1vNsqcaHVYJrKmgMcKsC1wcp8bujvXrHaAqD2iDYq3HoOwsxwo09Cuda5R5nC0o0IxlrlTuvPuzSw==}
@@ -1000,6 +1036,9 @@ packages:
'@jridgewell/trace-mapping@0.3.31':
resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==}
'@js-sdsl/ordered-map@4.4.2':
resolution: {integrity: sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==}
'@keyv/bigmap@1.3.0':
resolution: {integrity: sha512-KT01GjzV6AQD5+IYrcpoYLkCu1Jod3nau1Z7EsEuViO3TZGRacSbO9MfHmbJ1WaOXFtWLxPVj169cn2WNKPkIg==}
engines: {node: '>= 18'}
@@ -1494,6 +1533,174 @@ packages:
resolution: {integrity: sha512-da6KbdNCV5sr1/txD896V+6W0iamFWrvVl8cHkBSPT+YlvmT3DwXa4jxZnQc+gnuTEqSWbBeoSZYTayXH9wXcw==}
engines: {node: '>= 20'}
'@opentelemetry/api-logs@0.210.0':
resolution: {integrity: sha512-CMtLxp+lYDriveZejpBND/2TmadrrhUfChyxzmkFtHaMDdSKfP59MAYyA0ICBvEBdm3iXwLcaj/8Ic/pnGw9Yg==}
engines: {node: '>=8.0.0'}
'@opentelemetry/api@1.9.0':
resolution: {integrity: sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==}
engines: {node: '>=8.0.0'}
'@opentelemetry/configuration@0.210.0':
resolution: {integrity: sha512-tM0ROS/hZM72kB55cSjDcghVcUXBJdGkGzpkhD7M1B/gpcvZPSGfjFgKN3dgmxNgF76NxtbUwv3ik0wS+Kz52g==}
engines: {node: ^18.19.0 || >=20.6.0}
peerDependencies:
'@opentelemetry/api': ^1.9.0
'@opentelemetry/context-async-hooks@2.4.0':
resolution: {integrity: sha512-jn0phJ+hU7ZuvaoZE/8/Euw3gvHJrn2yi+kXrymwObEPVPjtwCmkvXDRQCWli+fCTTF/aSOtXaLr7CLIvv3LQg==}
engines: {node: ^18.19.0 || >=20.6.0}
peerDependencies:
'@opentelemetry/api': '>=1.0.0 <1.10.0'
'@opentelemetry/core@2.4.0':
resolution: {integrity: sha512-KtcyFHssTn5ZgDu6SXmUznS80OFs/wN7y6MyFRRcKU6TOw8hNcGxKvt8hsdaLJfhzUszNSjURetq5Qpkad14Gw==}
engines: {node: ^18.19.0 || >=20.6.0}
peerDependencies:
'@opentelemetry/api': '>=1.0.0 <1.10.0'
'@opentelemetry/exporter-logs-otlp-grpc@0.210.0':
resolution: {integrity: sha512-+BolenqOO6ow65go7uWRYPvvs/BBIWp1mtRn93VvGduqvMVH/IY8nXrt80a4L9hZ7lHi2Tq2/NcC3H2QzcWKag==}
engines: {node: ^18.19.0 || >=20.6.0}
peerDependencies:
'@opentelemetry/api': ^1.3.0
'@opentelemetry/exporter-logs-otlp-http@0.210.0':
resolution: {integrity: sha512-Q8/SEQtgrErbVVRg9M9iaG8m5wdPNdU0UOF7U43sAhwfmPG92ZOk/aenKhg0DXSNJHhkCDNCgS1kSoErAB3z0A==}
engines: {node: ^18.19.0 || >=20.6.0}
peerDependencies:
'@opentelemetry/api': ^1.3.0
'@opentelemetry/exporter-logs-otlp-proto@0.210.0':
resolution: {integrity: sha512-Y/yPc+gDhsWB7AsNzQWxblw4ULbvhCycMaQ2aAn+HSAVbgbMiZa0SbclPVHSnpnNzKSLVavFjweAr0pQA1KKLg==}
engines: {node: ^18.19.0 || >=20.6.0}
peerDependencies:
'@opentelemetry/api': ^1.3.0
'@opentelemetry/exporter-metrics-otlp-grpc@0.210.0':
resolution: {integrity: sha512-pWZ/Tjrqev9rdkqe8F6A9FGddLZrjl6iRAU5LBvvRL6I3PSgG8z1xM0cESAy1jzAF4wGohnAh8rB7hHzpUOYEA==}
engines: {node: ^18.19.0 || >=20.6.0}
peerDependencies:
'@opentelemetry/api': ^1.3.0
'@opentelemetry/exporter-metrics-otlp-http@0.210.0':
resolution: {integrity: sha512-JpLThG8Hh8A/Jzdzw9i4Ftu+EzvLaX/LouN+mOOHmadL0iror0Qsi3QWzucXeiUsDDsiYgjfKyi09e6sltytgA==}
engines: {node: ^18.19.0 || >=20.6.0}
peerDependencies:
'@opentelemetry/api': ^1.3.0
'@opentelemetry/exporter-metrics-otlp-proto@0.210.0':
resolution: {integrity: sha512-CFa7SOinYOVWIWJuQL7XFeyedzmFGIpHpSMNFE8Xefb6iGB4m+MukQecdssvPcJKYlfF5FpovEOLXwafAzsXWQ==}
engines: {node: ^18.19.0 || >=20.6.0}
peerDependencies:
'@opentelemetry/api': ^1.3.0
'@opentelemetry/exporter-prometheus@0.210.0':
resolution: {integrity: sha512-8i+7d70Hho6pcheTtbqIuS+bo+AIX/oNUTMwIEZoehUE4ZdbGmeVaE+hJS2LAErFeFaU71w164lAgYyMUEQ8zw==}
engines: {node: ^18.19.0 || >=20.6.0}
peerDependencies:
'@opentelemetry/api': ^1.3.0
'@opentelemetry/exporter-trace-otlp-grpc@0.210.0':
resolution: {integrity: sha512-1GPLOyxIfUX24WM8Oea+vx9d9TlewposUnsQXTjusxVMQ/dWvt5JIDJyTsfNDS412XRUOORgF97PwsfDY5QKGA==}
engines: {node: ^18.19.0 || >=20.6.0}
peerDependencies:
'@opentelemetry/api': ^1.3.0
'@opentelemetry/exporter-trace-otlp-http@0.210.0':
resolution: {integrity: sha512-9JkyaCl70anEtuKZdoCQmjDuz1/paEixY/DWfsvHt7PGKq3t8/nQ/6/xwxHjG+SkPAUbo1Iq4h7STe7Pk2bc5A==}
engines: {node: ^18.19.0 || >=20.6.0}
peerDependencies:
'@opentelemetry/api': ^1.3.0
'@opentelemetry/exporter-trace-otlp-proto@0.210.0':
resolution: {integrity: sha512-qVUY7Hsm/t5buGOtPcTV1Ch4W9kj2wGaQaAF5FO4XR8TMKl2GM45tUCnr0/1dF3wo4RG9khMxrddeQWdRL4fIg==}
engines: {node: ^18.19.0 || >=20.6.0}
peerDependencies:
'@opentelemetry/api': ^1.3.0
'@opentelemetry/exporter-zipkin@2.4.0':
resolution: {integrity: sha512-qpiXY0TUEFjBBp9b1na9LfuVQw6W8LH+te7uv+CC+0Up78ZDtZZwOjK2M7CL7Nspnw+yS4JdgEA7oxsBu0Ctsg==}
engines: {node: ^18.19.0 || >=20.6.0}
peerDependencies:
'@opentelemetry/api': ^1.0.0
'@opentelemetry/instrumentation@0.210.0':
resolution: {integrity: sha512-sLMhyHmW9katVaLUOKpfCnxSGhZq2t1ReWgwsu2cSgxmDVMB690H9TanuexanpFI94PJaokrqbp8u9KYZDUT5g==}
engines: {node: ^18.19.0 || >=20.6.0}
peerDependencies:
'@opentelemetry/api': ^1.3.0
'@opentelemetry/otlp-exporter-base@0.210.0':
resolution: {integrity: sha512-uk78DcZoBNHIm26h0oXc8Pizh4KDJ/y04N5k/UaI9J7xR7mL8QcMcYPQG9xxN7m8qotXOMDRW6qTAyptav4+3w==}
engines: {node: ^18.19.0 || >=20.6.0}
peerDependencies:
'@opentelemetry/api': ^1.3.0
'@opentelemetry/otlp-grpc-exporter-base@0.210.0':
resolution: {integrity: sha512-fEJs8UhkFMrdXMOCLXyKd2uc6N209tIi8IBNqSTi83ri+MlMFrBKnOtklmv9/zzxovoN5zD1waRt6XBFGPfmIw==}
engines: {node: ^18.19.0 || >=20.6.0}
peerDependencies:
'@opentelemetry/api': ^1.3.0
'@opentelemetry/otlp-transformer@0.210.0':
resolution: {integrity: sha512-nkHBJVSJGOwkRZl+BFIr7gikA93/U8XkL2EWaiDbj3DVjmTEZQpegIKk0lT8oqQYfP8FC6zWNjuTfkaBVqa0ZQ==}
engines: {node: ^18.19.0 || >=20.6.0}
peerDependencies:
'@opentelemetry/api': ^1.3.0
'@opentelemetry/propagator-b3@2.4.0':
resolution: {integrity: sha512-6VPsFiMUkJBre/86F0d+PZMaUCcuLA9DtZuC46KH8EeVEKZPEM2WlX35M/qmde8UpzoQL9qzdz54YjUYABt8Uw==}
engines: {node: ^18.19.0 || >=20.6.0}
peerDependencies:
'@opentelemetry/api': '>=1.0.0 <1.10.0'
'@opentelemetry/propagator-jaeger@2.4.0':
resolution: {integrity: sha512-t6muBL/3AMD++1EMF658C/KIpj3gfmTmftX3mEQql4KIxNGFvacCmmTtrQt9IZAJmQRfjQRCkv+vsGbQugeJIw==}
engines: {node: ^18.19.0 || >=20.6.0}
peerDependencies:
'@opentelemetry/api': '>=1.0.0 <1.10.0'
'@opentelemetry/resources@2.4.0':
resolution: {integrity: sha512-RWvGLj2lMDZd7M/5tjkI/2VHMpXebLgPKvBUd9LRasEWR2xAynDwEYZuLvY9P2NGG73HF07jbbgWX2C9oavcQg==}
engines: {node: ^18.19.0 || >=20.6.0}
peerDependencies:
'@opentelemetry/api': '>=1.3.0 <1.10.0'
'@opentelemetry/sdk-logs@0.210.0':
resolution: {integrity: sha512-YuaL92Dpyk/Kc1o4e9XiaWWwiC0aBFN+4oy+6A9TP4UNJmRymPMEX10r6EMMFMD7V0hktiSig9cwWo59peeLCQ==}
engines: {node: ^18.19.0 || >=20.6.0}
peerDependencies:
'@opentelemetry/api': '>=1.4.0 <1.10.0'
'@opentelemetry/sdk-metrics@2.4.0':
resolution: {integrity: sha512-qSbfq9mXbLMqmPEjijl32f3ZEmiHekebRggPdPjhHI6t1CsAQOR2Aw/SuTDftk3/l2aaPHpwP3xM2DkgBA1ANw==}
engines: {node: ^18.19.0 || >=20.6.0}
peerDependencies:
'@opentelemetry/api': '>=1.9.0 <1.10.0'
'@opentelemetry/sdk-node@0.210.0':
resolution: {integrity: sha512-KymqUtYvfpblDNgGxBXYqCcDjYXwjOF7Muc6ocs0rMlG/66Hcs9KiJ7hg4zLOv63JubF/vxi5WXaLrQrPKyaZQ==}
engines: {node: ^18.19.0 || >=20.6.0}
peerDependencies:
'@opentelemetry/api': '>=1.3.0 <1.10.0'
'@opentelemetry/sdk-trace-base@2.4.0':
resolution: {integrity: sha512-WH0xXkz/OHORDLKqaxcUZS0X+t1s7gGlumr2ebiEgNZQl2b0upK2cdoD0tatf7l8iP74woGJ/Kmxe82jdvcWRw==}
engines: {node: ^18.19.0 || >=20.6.0}
peerDependencies:
'@opentelemetry/api': '>=1.3.0 <1.10.0'
'@opentelemetry/sdk-trace-node@2.4.0':
resolution: {integrity: sha512-MBc2l04hZPYygnWPT38UiOPy9ueutPqmJ47z0m9IKuoVQh3MblmbSgwspjhdHagZLfSfmlzhWR1xtbgVNmjX2A==}
engines: {node: ^18.19.0 || >=20.6.0}
peerDependencies:
'@opentelemetry/api': '>=1.0.0 <1.10.0'
'@opentelemetry/semantic-conventions@1.39.0':
resolution: {integrity: sha512-R5R9tb2AXs2IRLNKLBJDynhkfmx7mX0vi8NkhZb3gUkPWHn6HXk5J8iQ/dql0U3ApfWym4kXXmBDRGO+oeOfjg==}
engines: {node: '>=14'}
'@oxc-project/types@0.108.0':
resolution: {integrity: sha512-7lf13b2IA/kZO6xgnIZA88sq3vwrxWk+2vxf6cc+omwYCRTiA5e63Beqf3fz/v8jEviChWWmFYBwzfSeyrsj7Q==}
@@ -2376,6 +2583,16 @@ packages:
resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==}
engines: {node: '>= 0.6'}
acorn-import-attributes@1.9.5:
resolution: {integrity: sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==}
peerDependencies:
acorn: ^8
acorn@8.15.0:
resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==}
engines: {node: '>=0.4.0'}
hasBin: true
agent-base@7.1.4:
resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==}
engines: {node: '>= 14'}
@@ -2633,6 +2850,9 @@ packages:
resolution: {integrity: sha512-Wdy2Igu8OcBpI2pZePZ5oWjPC38tmDVx5WKUXKwlLYkA0ozo85sLsLvkBbBn/sZaSCMFOGZJ14fvW9t5/d7kdA==}
engines: {node: '>=8'}
cjs-module-lexer@2.2.0:
resolution: {integrity: sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ==}
class-variance-authority@0.7.1:
resolution: {integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==}
@@ -3269,6 +3489,9 @@ packages:
immediate@3.0.6:
resolution: {integrity: sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==}
import-in-the-middle@2.0.4:
resolution: {integrity: sha512-Al0kMpa0BqfvDnxjxGlab9vdQ0vTDs82TBKrD59X9jReUoPAzSGBb6vGDzMUMFBGyyDF03RpLT4oxGn6BpASzQ==}
inherits@2.0.4:
resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
@@ -3759,6 +3982,9 @@ packages:
engines: {node: '>=10'}
hasBin: true
module-details-from-path@1.0.4:
resolution: {integrity: sha512-EGWKgxALGMgzvxYF1UyGTy0HXX/2vHLkw6+NvDKW2jypWbHpjQuj4UMcqQWXHERJhVGKikolT06G3bcKe4fi7w==}
morgan@1.10.1:
resolution: {integrity: sha512-223dMRJtI/l25dJKWpgij2cMtywuG/WiUKXdvwfbhGKBhy1puASqXwFzmWZ7+K73vUPoR7SS2Qz2cI/g9MKw0A==}
engines: {node: '>= 0.8.0'}
@@ -4161,6 +4387,10 @@ packages:
resolution: {integrity: sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg==}
engines: {node: '>=12.0.0'}
protobufjs@8.0.0:
resolution: {integrity: sha512-jx6+sE9h/UryaCZhsJWbJtTEy47yXoGNYI4z8ZaRncM0zBKeRqjO2JEcOUYwrYGb1WLhXM1FfMzW3annvFv0rw==}
engines: {node: '>=12.0.0'}
proxy-addr@2.0.7:
resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==}
engines: {node: '>= 0.10'}
@@ -4275,6 +4505,10 @@ packages:
resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==}
engines: {node: '>=0.10.0'}
require-in-the-middle@8.0.1:
resolution: {integrity: sha512-QT7FVMXfWOYFbeRBF6nu+I6tr2Tf3u0q8RIEjNob/heKY/nh7drD/k7eeMFmSQgnTtCzLDcCu/XEnpW2wk4xCQ==}
engines: {node: '>=9.3.0 || >=8.10.0 <9.0.0'}
resolve-pkg-maps@1.0.0:
resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==}
@@ -5632,6 +5866,18 @@ snapshots:
'@grammyjs/types@3.23.0': {}
'@grpc/grpc-js@1.14.3':
dependencies:
'@grpc/proto-loader': 0.8.0
'@js-sdsl/ordered-map': 4.4.2
'@grpc/proto-loader@0.8.0':
dependencies:
lodash.camelcase: 4.3.0
long: 5.3.2
protobufjs: 7.5.4
yargs: 17.7.2
'@hapi/boom@9.1.4':
dependencies:
'@hapi/hoek': 9.3.0
@@ -5779,6 +6025,8 @@ snapshots:
'@jridgewell/resolve-uri': 3.1.2
'@jridgewell/sourcemap-codec': 1.5.5
'@js-sdsl/ordered-map@4.4.2': {}
'@keyv/bigmap@1.3.0(keyv@5.5.5)':
dependencies:
hashery: 1.4.0
@@ -6333,6 +6581,241 @@ snapshots:
'@octokit/webhooks-methods': 6.0.0
optional: true
'@opentelemetry/api-logs@0.210.0':
dependencies:
'@opentelemetry/api': 1.9.0
'@opentelemetry/api@1.9.0': {}
'@opentelemetry/configuration@0.210.0(@opentelemetry/api@1.9.0)':
dependencies:
'@opentelemetry/api': 1.9.0
'@opentelemetry/core': 2.4.0(@opentelemetry/api@1.9.0)
yaml: 2.8.2
'@opentelemetry/context-async-hooks@2.4.0(@opentelemetry/api@1.9.0)':
dependencies:
'@opentelemetry/api': 1.9.0
'@opentelemetry/core@2.4.0(@opentelemetry/api@1.9.0)':
dependencies:
'@opentelemetry/api': 1.9.0
'@opentelemetry/semantic-conventions': 1.39.0
'@opentelemetry/exporter-logs-otlp-grpc@0.210.0(@opentelemetry/api@1.9.0)':
dependencies:
'@grpc/grpc-js': 1.14.3
'@opentelemetry/api': 1.9.0
'@opentelemetry/core': 2.4.0(@opentelemetry/api@1.9.0)
'@opentelemetry/otlp-exporter-base': 0.210.0(@opentelemetry/api@1.9.0)
'@opentelemetry/otlp-grpc-exporter-base': 0.210.0(@opentelemetry/api@1.9.0)
'@opentelemetry/otlp-transformer': 0.210.0(@opentelemetry/api@1.9.0)
'@opentelemetry/sdk-logs': 0.210.0(@opentelemetry/api@1.9.0)
'@opentelemetry/exporter-logs-otlp-http@0.210.0(@opentelemetry/api@1.9.0)':
dependencies:
'@opentelemetry/api': 1.9.0
'@opentelemetry/api-logs': 0.210.0
'@opentelemetry/core': 2.4.0(@opentelemetry/api@1.9.0)
'@opentelemetry/otlp-exporter-base': 0.210.0(@opentelemetry/api@1.9.0)
'@opentelemetry/otlp-transformer': 0.210.0(@opentelemetry/api@1.9.0)
'@opentelemetry/sdk-logs': 0.210.0(@opentelemetry/api@1.9.0)
'@opentelemetry/exporter-logs-otlp-proto@0.210.0(@opentelemetry/api@1.9.0)':
dependencies:
'@opentelemetry/api': 1.9.0
'@opentelemetry/api-logs': 0.210.0
'@opentelemetry/core': 2.4.0(@opentelemetry/api@1.9.0)
'@opentelemetry/otlp-exporter-base': 0.210.0(@opentelemetry/api@1.9.0)
'@opentelemetry/otlp-transformer': 0.210.0(@opentelemetry/api@1.9.0)
'@opentelemetry/resources': 2.4.0(@opentelemetry/api@1.9.0)
'@opentelemetry/sdk-logs': 0.210.0(@opentelemetry/api@1.9.0)
'@opentelemetry/sdk-trace-base': 2.4.0(@opentelemetry/api@1.9.0)
'@opentelemetry/exporter-metrics-otlp-grpc@0.210.0(@opentelemetry/api@1.9.0)':
dependencies:
'@grpc/grpc-js': 1.14.3
'@opentelemetry/api': 1.9.0
'@opentelemetry/core': 2.4.0(@opentelemetry/api@1.9.0)
'@opentelemetry/exporter-metrics-otlp-http': 0.210.0(@opentelemetry/api@1.9.0)
'@opentelemetry/otlp-exporter-base': 0.210.0(@opentelemetry/api@1.9.0)
'@opentelemetry/otlp-grpc-exporter-base': 0.210.0(@opentelemetry/api@1.9.0)
'@opentelemetry/otlp-transformer': 0.210.0(@opentelemetry/api@1.9.0)
'@opentelemetry/resources': 2.4.0(@opentelemetry/api@1.9.0)
'@opentelemetry/sdk-metrics': 2.4.0(@opentelemetry/api@1.9.0)
'@opentelemetry/exporter-metrics-otlp-http@0.210.0(@opentelemetry/api@1.9.0)':
dependencies:
'@opentelemetry/api': 1.9.0
'@opentelemetry/core': 2.4.0(@opentelemetry/api@1.9.0)
'@opentelemetry/otlp-exporter-base': 0.210.0(@opentelemetry/api@1.9.0)
'@opentelemetry/otlp-transformer': 0.210.0(@opentelemetry/api@1.9.0)
'@opentelemetry/resources': 2.4.0(@opentelemetry/api@1.9.0)
'@opentelemetry/sdk-metrics': 2.4.0(@opentelemetry/api@1.9.0)
'@opentelemetry/exporter-metrics-otlp-proto@0.210.0(@opentelemetry/api@1.9.0)':
dependencies:
'@opentelemetry/api': 1.9.0
'@opentelemetry/core': 2.4.0(@opentelemetry/api@1.9.0)
'@opentelemetry/exporter-metrics-otlp-http': 0.210.0(@opentelemetry/api@1.9.0)
'@opentelemetry/otlp-exporter-base': 0.210.0(@opentelemetry/api@1.9.0)
'@opentelemetry/otlp-transformer': 0.210.0(@opentelemetry/api@1.9.0)
'@opentelemetry/resources': 2.4.0(@opentelemetry/api@1.9.0)
'@opentelemetry/sdk-metrics': 2.4.0(@opentelemetry/api@1.9.0)
'@opentelemetry/exporter-prometheus@0.210.0(@opentelemetry/api@1.9.0)':
dependencies:
'@opentelemetry/api': 1.9.0
'@opentelemetry/core': 2.4.0(@opentelemetry/api@1.9.0)
'@opentelemetry/resources': 2.4.0(@opentelemetry/api@1.9.0)
'@opentelemetry/sdk-metrics': 2.4.0(@opentelemetry/api@1.9.0)
'@opentelemetry/exporter-trace-otlp-grpc@0.210.0(@opentelemetry/api@1.9.0)':
dependencies:
'@grpc/grpc-js': 1.14.3
'@opentelemetry/api': 1.9.0
'@opentelemetry/core': 2.4.0(@opentelemetry/api@1.9.0)
'@opentelemetry/otlp-exporter-base': 0.210.0(@opentelemetry/api@1.9.0)
'@opentelemetry/otlp-grpc-exporter-base': 0.210.0(@opentelemetry/api@1.9.0)
'@opentelemetry/otlp-transformer': 0.210.0(@opentelemetry/api@1.9.0)
'@opentelemetry/resources': 2.4.0(@opentelemetry/api@1.9.0)
'@opentelemetry/sdk-trace-base': 2.4.0(@opentelemetry/api@1.9.0)
'@opentelemetry/exporter-trace-otlp-http@0.210.0(@opentelemetry/api@1.9.0)':
dependencies:
'@opentelemetry/api': 1.9.0
'@opentelemetry/core': 2.4.0(@opentelemetry/api@1.9.0)
'@opentelemetry/otlp-exporter-base': 0.210.0(@opentelemetry/api@1.9.0)
'@opentelemetry/otlp-transformer': 0.210.0(@opentelemetry/api@1.9.0)
'@opentelemetry/resources': 2.4.0(@opentelemetry/api@1.9.0)
'@opentelemetry/sdk-trace-base': 2.4.0(@opentelemetry/api@1.9.0)
'@opentelemetry/exporter-trace-otlp-proto@0.210.0(@opentelemetry/api@1.9.0)':
dependencies:
'@opentelemetry/api': 1.9.0
'@opentelemetry/core': 2.4.0(@opentelemetry/api@1.9.0)
'@opentelemetry/otlp-exporter-base': 0.210.0(@opentelemetry/api@1.9.0)
'@opentelemetry/otlp-transformer': 0.210.0(@opentelemetry/api@1.9.0)
'@opentelemetry/resources': 2.4.0(@opentelemetry/api@1.9.0)
'@opentelemetry/sdk-trace-base': 2.4.0(@opentelemetry/api@1.9.0)
'@opentelemetry/exporter-zipkin@2.4.0(@opentelemetry/api@1.9.0)':
dependencies:
'@opentelemetry/api': 1.9.0
'@opentelemetry/core': 2.4.0(@opentelemetry/api@1.9.0)
'@opentelemetry/resources': 2.4.0(@opentelemetry/api@1.9.0)
'@opentelemetry/sdk-trace-base': 2.4.0(@opentelemetry/api@1.9.0)
'@opentelemetry/semantic-conventions': 1.39.0
'@opentelemetry/instrumentation@0.210.0(@opentelemetry/api@1.9.0)':
dependencies:
'@opentelemetry/api': 1.9.0
'@opentelemetry/api-logs': 0.210.0
import-in-the-middle: 2.0.4
require-in-the-middle: 8.0.1
transitivePeerDependencies:
- supports-color
'@opentelemetry/otlp-exporter-base@0.210.0(@opentelemetry/api@1.9.0)':
dependencies:
'@opentelemetry/api': 1.9.0
'@opentelemetry/core': 2.4.0(@opentelemetry/api@1.9.0)
'@opentelemetry/otlp-transformer': 0.210.0(@opentelemetry/api@1.9.0)
'@opentelemetry/otlp-grpc-exporter-base@0.210.0(@opentelemetry/api@1.9.0)':
dependencies:
'@grpc/grpc-js': 1.14.3
'@opentelemetry/api': 1.9.0
'@opentelemetry/core': 2.4.0(@opentelemetry/api@1.9.0)
'@opentelemetry/otlp-exporter-base': 0.210.0(@opentelemetry/api@1.9.0)
'@opentelemetry/otlp-transformer': 0.210.0(@opentelemetry/api@1.9.0)
'@opentelemetry/otlp-transformer@0.210.0(@opentelemetry/api@1.9.0)':
dependencies:
'@opentelemetry/api': 1.9.0
'@opentelemetry/api-logs': 0.210.0
'@opentelemetry/core': 2.4.0(@opentelemetry/api@1.9.0)
'@opentelemetry/resources': 2.4.0(@opentelemetry/api@1.9.0)
'@opentelemetry/sdk-logs': 0.210.0(@opentelemetry/api@1.9.0)
'@opentelemetry/sdk-metrics': 2.4.0(@opentelemetry/api@1.9.0)
'@opentelemetry/sdk-trace-base': 2.4.0(@opentelemetry/api@1.9.0)
protobufjs: 8.0.0
'@opentelemetry/propagator-b3@2.4.0(@opentelemetry/api@1.9.0)':
dependencies:
'@opentelemetry/api': 1.9.0
'@opentelemetry/core': 2.4.0(@opentelemetry/api@1.9.0)
'@opentelemetry/propagator-jaeger@2.4.0(@opentelemetry/api@1.9.0)':
dependencies:
'@opentelemetry/api': 1.9.0
'@opentelemetry/core': 2.4.0(@opentelemetry/api@1.9.0)
'@opentelemetry/resources@2.4.0(@opentelemetry/api@1.9.0)':
dependencies:
'@opentelemetry/api': 1.9.0
'@opentelemetry/core': 2.4.0(@opentelemetry/api@1.9.0)
'@opentelemetry/semantic-conventions': 1.39.0
'@opentelemetry/sdk-logs@0.210.0(@opentelemetry/api@1.9.0)':
dependencies:
'@opentelemetry/api': 1.9.0
'@opentelemetry/api-logs': 0.210.0
'@opentelemetry/core': 2.4.0(@opentelemetry/api@1.9.0)
'@opentelemetry/resources': 2.4.0(@opentelemetry/api@1.9.0)
'@opentelemetry/sdk-metrics@2.4.0(@opentelemetry/api@1.9.0)':
dependencies:
'@opentelemetry/api': 1.9.0
'@opentelemetry/core': 2.4.0(@opentelemetry/api@1.9.0)
'@opentelemetry/resources': 2.4.0(@opentelemetry/api@1.9.0)
'@opentelemetry/sdk-node@0.210.0(@opentelemetry/api@1.9.0)':
dependencies:
'@opentelemetry/api': 1.9.0
'@opentelemetry/api-logs': 0.210.0
'@opentelemetry/configuration': 0.210.0(@opentelemetry/api@1.9.0)
'@opentelemetry/context-async-hooks': 2.4.0(@opentelemetry/api@1.9.0)
'@opentelemetry/core': 2.4.0(@opentelemetry/api@1.9.0)
'@opentelemetry/exporter-logs-otlp-grpc': 0.210.0(@opentelemetry/api@1.9.0)
'@opentelemetry/exporter-logs-otlp-http': 0.210.0(@opentelemetry/api@1.9.0)
'@opentelemetry/exporter-logs-otlp-proto': 0.210.0(@opentelemetry/api@1.9.0)
'@opentelemetry/exporter-metrics-otlp-grpc': 0.210.0(@opentelemetry/api@1.9.0)
'@opentelemetry/exporter-metrics-otlp-http': 0.210.0(@opentelemetry/api@1.9.0)
'@opentelemetry/exporter-metrics-otlp-proto': 0.210.0(@opentelemetry/api@1.9.0)
'@opentelemetry/exporter-prometheus': 0.210.0(@opentelemetry/api@1.9.0)
'@opentelemetry/exporter-trace-otlp-grpc': 0.210.0(@opentelemetry/api@1.9.0)
'@opentelemetry/exporter-trace-otlp-http': 0.210.0(@opentelemetry/api@1.9.0)
'@opentelemetry/exporter-trace-otlp-proto': 0.210.0(@opentelemetry/api@1.9.0)
'@opentelemetry/exporter-zipkin': 2.4.0(@opentelemetry/api@1.9.0)
'@opentelemetry/instrumentation': 0.210.0(@opentelemetry/api@1.9.0)
'@opentelemetry/propagator-b3': 2.4.0(@opentelemetry/api@1.9.0)
'@opentelemetry/propagator-jaeger': 2.4.0(@opentelemetry/api@1.9.0)
'@opentelemetry/resources': 2.4.0(@opentelemetry/api@1.9.0)
'@opentelemetry/sdk-logs': 0.210.0(@opentelemetry/api@1.9.0)
'@opentelemetry/sdk-metrics': 2.4.0(@opentelemetry/api@1.9.0)
'@opentelemetry/sdk-trace-base': 2.4.0(@opentelemetry/api@1.9.0)
'@opentelemetry/sdk-trace-node': 2.4.0(@opentelemetry/api@1.9.0)
'@opentelemetry/semantic-conventions': 1.39.0
transitivePeerDependencies:
- supports-color
'@opentelemetry/sdk-trace-base@2.4.0(@opentelemetry/api@1.9.0)':
dependencies:
'@opentelemetry/api': 1.9.0
'@opentelemetry/core': 2.4.0(@opentelemetry/api@1.9.0)
'@opentelemetry/resources': 2.4.0(@opentelemetry/api@1.9.0)
'@opentelemetry/semantic-conventions': 1.39.0
'@opentelemetry/sdk-trace-node@2.4.0(@opentelemetry/api@1.9.0)':
dependencies:
'@opentelemetry/api': 1.9.0
'@opentelemetry/context-async-hooks': 2.4.0(@opentelemetry/api@1.9.0)
'@opentelemetry/core': 2.4.0(@opentelemetry/api@1.9.0)
'@opentelemetry/sdk-trace-base': 2.4.0(@opentelemetry/api@1.9.0)
'@opentelemetry/semantic-conventions@1.39.0': {}
'@oxc-project/types@0.108.0': {}
'@oxfmt/darwin-arm64@0.26.0':
@@ -7145,7 +7628,7 @@ snapshots:
'@vitest/mocker': 4.0.17(vite@7.3.1(@types/node@25.0.9)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2))
playwright: 1.57.0
tinyrainbow: 3.0.3
vitest: 4.0.17(@types/node@25.0.9)(@vitest/browser-playwright@4.0.17)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2)
vitest: 4.0.17(@opentelemetry/api@1.9.0)(@types/node@25.0.9)(@vitest/browser-playwright@4.0.17)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2)
transitivePeerDependencies:
- bufferutil
- msw
@@ -7161,7 +7644,7 @@ snapshots:
pngjs: 7.0.0
sirv: 3.0.2
tinyrainbow: 3.0.3
vitest: 4.0.17(@types/node@25.0.9)(@vitest/browser-playwright@4.0.17)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2)
vitest: 4.0.17(@opentelemetry/api@1.9.0)(@types/node@25.0.9)(@vitest/browser-playwright@4.0.17)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2)
ws: 8.19.0
transitivePeerDependencies:
- bufferutil
@@ -7181,7 +7664,7 @@ snapshots:
obug: 2.1.1
std-env: 3.10.0
tinyrainbow: 3.0.3
vitest: 4.0.17(@types/node@25.0.9)(@vitest/browser-playwright@4.0.17)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2)
vitest: 4.0.17(@opentelemetry/api@1.9.0)(@types/node@25.0.9)(@vitest/browser-playwright@4.0.17)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2)
optionalDependencies:
'@vitest/browser': 4.0.17(vite@7.3.1(@types/node@25.0.9)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2))(vitest@4.0.17)
@@ -7290,6 +7773,12 @@ snapshots:
mime-types: 3.0.2
negotiator: 1.0.0
acorn-import-attributes@1.9.5(acorn@8.15.0):
dependencies:
acorn: 8.15.0
acorn@8.15.0: {}
agent-base@7.1.4: {}
ajv-formats@3.0.1(ajv@8.17.1):
@@ -7575,6 +8064,8 @@ snapshots:
ci-info@4.3.1:
optional: true
cjs-module-lexer@2.2.0: {}
class-variance-authority@0.7.1:
dependencies:
clsx: 2.1.1
@@ -7607,7 +8098,6 @@ snapshots:
string-width: 4.2.3
strip-ansi: 6.0.1
wrap-ansi: 7.0.0
optional: true
clsx@2.1.1: {}
@@ -8323,6 +8813,13 @@ snapshots:
immediate@3.0.6: {}
import-in-the-middle@2.0.4:
dependencies:
acorn: 8.15.0
acorn-import-attributes: 1.9.5(acorn@8.15.0)
cjs-module-lexer: 2.2.0
module-details-from-path: 1.0.4
inherits@2.0.4: {}
ini@1.3.8:
@@ -8804,6 +9301,8 @@ snapshots:
mkdirp@3.0.1: {}
module-details-from-path@1.0.4: {}
morgan@1.10.1:
dependencies:
basic-auth: 2.0.1
@@ -9255,6 +9754,21 @@ snapshots:
'@types/node': 25.0.9
long: 5.3.2
protobufjs@8.0.0:
dependencies:
'@protobufjs/aspromise': 1.1.2
'@protobufjs/base64': 1.1.2
'@protobufjs/codegen': 2.0.4
'@protobufjs/eventemitter': 1.1.0
'@protobufjs/fetch': 1.1.0
'@protobufjs/float': 1.0.2
'@protobufjs/inquire': 1.1.0
'@protobufjs/path': 1.1.2
'@protobufjs/pool': 1.1.0
'@protobufjs/utf8': 1.1.0
'@types/node': 25.0.9
long: 5.3.2
proxy-addr@2.0.7:
dependencies:
forwarded: 0.2.0
@@ -9409,6 +9923,13 @@ snapshots:
require-from-string@2.0.2: {}
require-in-the-middle@8.0.1:
dependencies:
debug: 4.4.3
module-details-from-path: 1.0.4
transitivePeerDependencies:
- supports-color
resolve-pkg-maps@1.0.0: {}
restore-cursor@5.1.0:
@@ -9998,7 +10519,7 @@ snapshots:
tsx: 4.21.0
yaml: 2.8.2
vitest@4.0.17(@types/node@25.0.9)(@vitest/browser-playwright@4.0.17)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2):
vitest@4.0.17(@opentelemetry/api@1.9.0)(@types/node@25.0.9)(@vitest/browser-playwright@4.0.17)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2):
dependencies:
'@vitest/expect': 4.0.17
'@vitest/mocker': 4.0.17(vite@7.3.1(@types/node@25.0.9)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2))
@@ -10021,6 +10542,7 @@ snapshots:
vite: 7.3.1(@types/node@25.0.9)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2)
why-is-node-running: 2.3.0
optionalDependencies:
'@opentelemetry/api': 1.9.0
'@types/node': 25.0.9
'@vitest/browser-playwright': 4.0.17(playwright@1.57.0)(vite@7.3.1(@types/node@25.0.9)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2))(vitest@4.0.17)
transitivePeerDependencies:
@@ -10109,8 +10631,7 @@ snapshots:
yargs-parser@20.2.9: {}
yargs-parser@21.1.1:
optional: true
yargs-parser@21.1.1: {}
yargs@16.2.0:
dependencies:
@@ -10131,7 +10652,6 @@ snapshots:
string-width: 4.2.3
y18n: 5.0.8
yargs-parser: 21.1.1
optional: true
yoctocolors@2.1.2: {}

View File

@@ -18,7 +18,7 @@ import {
import type { TypingMode } from "../../config/types.js";
import { logVerbose } from "../../globals.js";
import { defaultRuntime } from "../../runtime.js";
import { resolveModelCostConfig } from "../../utils/usage-format.js";
import { estimateUsageCost, resolveModelCostConfig } from "../../utils/usage-format.js";
import type { OriginatingChannelType, TemplateContext } from "../templating.js";
import { resolveResponseUsageMode, type VerboseLevel } from "../thinking.js";
import type { GetReplyOptions, ReplyPayload } from "../types.js";
@@ -41,6 +41,7 @@ import { createReplyToModeFilterForChannel, resolveReplyToMode } from "./reply-t
import { incrementCompactionCount } from "./session-updates.js";
import type { TypingController } from "./typing.js";
import { createTypingSignaler } from "./typing-mode.js";
import { emitDiagnosticEvent, isDiagnosticsEnabled } from "../../infra/diagnostic-events.js";
const BLOCK_REPLY_SEND_TIMEOUT_MS = 15_000;
@@ -296,6 +297,7 @@ export async function runReplyAgent(params: {
cleanupTranscripts: true,
});
try {
const runStartedAt = Date.now();
const runOutcome = await runAgentTurnWithFallback({
commandBody,
followupRun,
@@ -403,6 +405,43 @@ export async function runReplyAgent(params: {
activeSessionEntry?.contextTokens ??
DEFAULT_CONTEXT_TOKENS;
if (isDiagnosticsEnabled(cfg) && hasNonzeroUsage(usage)) {
const input = usage.input ?? 0;
const output = usage.output ?? 0;
const cacheRead = usage.cacheRead ?? 0;
const cacheWrite = usage.cacheWrite ?? 0;
const promptTokens = input + cacheRead + cacheWrite;
const totalTokens = usage.total ?? promptTokens + output;
const costConfig = resolveModelCostConfig({
provider: providerUsed,
model: modelUsed,
config: cfg,
});
const costUsd = estimateUsageCost({ usage, cost: costConfig });
emitDiagnosticEvent({
type: "model.usage",
sessionKey,
sessionId: followupRun.run.sessionId,
channel: replyToChannel,
provider: providerUsed,
model: modelUsed,
usage: {
input,
output,
cacheRead,
cacheWrite,
promptTokens,
total: totalTokens,
},
context: {
limit: contextTokensUsed,
used: totalTokens,
},
costUsd,
durationMs: Date.now() - runStartedAt,
});
}
if (storePath && sessionKey) {
if (hasNonzeroUsage(usage)) {
try {

View File

@@ -47,6 +47,7 @@ export type ChannelUiMetadata = {
const GROUP_LABELS: Record<string, string> = {
wizard: "Wizard",
update: "Update",
diagnostics: "Diagnostics",
logging: "Logging",
gateway: "Gateway",
agents: "Agents",
@@ -73,6 +74,7 @@ const GROUP_LABELS: Record<string, string> = {
const GROUP_ORDER: Record<string, number> = {
wizard: 20,
update: 25,
diagnostics: 27,
gateway: 30,
agents: 40,
tools: 50,
@@ -101,6 +103,17 @@ const FIELD_LABELS: Record<string, string> = {
"meta.lastTouchedAt": "Config Last Touched At",
"update.channel": "Update Channel",
"update.checkOnStart": "Update Check on Start",
"diagnostics.enabled": "Diagnostics Enabled",
"diagnostics.otel.enabled": "OpenTelemetry Enabled",
"diagnostics.otel.endpoint": "OpenTelemetry Endpoint",
"diagnostics.otel.protocol": "OpenTelemetry Protocol",
"diagnostics.otel.headers": "OpenTelemetry Headers",
"diagnostics.otel.serviceName": "OpenTelemetry Service Name",
"diagnostics.otel.traces": "OpenTelemetry Traces Enabled",
"diagnostics.otel.metrics": "OpenTelemetry Metrics Enabled",
"diagnostics.otel.logs": "OpenTelemetry Logs Enabled",
"diagnostics.otel.sampleRate": "OpenTelemetry Trace Sample Rate",
"diagnostics.otel.flushIntervalMs": "OpenTelemetry Flush Interval (ms)",
"gateway.remote.url": "Remote Gateway URL",
"gateway.remote.sshTarget": "Remote Gateway SSH Target",
"gateway.remote.sshIdentity": "Remote Gateway SSH Identity",

View File

@@ -102,6 +102,26 @@ export type LoggingConfig = {
redactPatterns?: string[];
};
export type DiagnosticsOtelConfig = {
enabled?: boolean;
endpoint?: string;
protocol?: "http/protobuf" | "grpc";
headers?: Record<string, string>;
serviceName?: string;
traces?: boolean;
metrics?: boolean;
logs?: boolean;
/** Trace sample rate (0.0 - 1.0). */
sampleRate?: number;
/** Metric export interval (ms). */
flushIntervalMs?: number;
};
export type DiagnosticsConfig = {
enabled?: boolean;
otel?: DiagnosticsOtelConfig;
};
export type WebReconnectConfig = {
initialMs?: number;
maxMs?: number;

View File

@@ -1,6 +1,6 @@
import type { AgentBinding, AgentsConfig } from "./types.agents.js";
import type { AuthConfig } from "./types.auth.js";
import type { LoggingConfig, SessionConfig, WebConfig } from "./types.base.js";
import type { DiagnosticsConfig, LoggingConfig, SessionConfig, WebConfig } from "./types.base.js";
import type { BrowserConfig } from "./types.browser.js";
import type { ChannelsConfig } from "./types.channels.js";
import type { CronConfig } from "./types.cron.js";
@@ -53,6 +53,7 @@ export type ClawdbotConfig = {
lastRunCommand?: string;
lastRunMode?: "local" | "remote";
};
diagnostics?: DiagnosticsConfig;
logging?: LoggingConfig;
update?: {
/** Update channel for git + npm installs ("stable", "beta", or "dev"). */

View File

@@ -38,6 +38,27 @@ export const ClawdbotSchema = z
})
.strict()
.optional(),
diagnostics: z
.object({
enabled: z.boolean().optional(),
otel: z
.object({
enabled: z.boolean().optional(),
endpoint: z.string().optional(),
protocol: z.union([z.literal("http/protobuf"), z.literal("grpc")]).optional(),
headers: z.record(z.string(), z.string()).optional(),
serviceName: z.string().optional(),
traces: z.boolean().optional(),
metrics: z.boolean().optional(),
logs: z.boolean().optional(),
sampleRate: z.number().min(0).max(1).optional(),
flushIntervalMs: z.number().int().nonnegative().optional(),
})
.strict()
.optional(),
})
.strict()
.optional(),
logging: z
.object({
level: z

View File

@@ -0,0 +1,28 @@
import { describe, expect, test } from "vitest";
import {
emitDiagnosticEvent,
onDiagnosticEvent,
resetDiagnosticEventsForTest,
} from "./diagnostic-events.js";
describe("diagnostic-events", () => {
test("emits monotonic seq", async () => {
resetDiagnosticEventsForTest();
const seqs: number[] = [];
const stop = onDiagnosticEvent((evt) => seqs.push(evt.seq));
emitDiagnosticEvent({
type: "model.usage",
usage: { total: 1 },
});
emitDiagnosticEvent({
type: "model.usage",
usage: { total: 2 },
});
stop();
expect(seqs).toEqual([1, 2]);
});
});

View File

@@ -0,0 +1,60 @@
import type { ClawdbotConfig } from "../config/config.js";
export type DiagnosticUsageEvent = {
type: "model.usage";
ts: number;
seq: number;
sessionKey?: string;
sessionId?: string;
channel?: string;
provider?: string;
model?: string;
usage: {
input?: number;
output?: number;
cacheRead?: number;
cacheWrite?: number;
promptTokens?: number;
total?: number;
};
context?: {
limit?: number;
used?: number;
};
costUsd?: number;
durationMs?: number;
};
export type DiagnosticEventPayload = DiagnosticUsageEvent;
let seq = 0;
const listeners = new Set<(evt: DiagnosticEventPayload) => void>();
export function isDiagnosticsEnabled(config?: ClawdbotConfig): boolean {
return config?.diagnostics?.enabled === true;
}
export function emitDiagnosticEvent(event: Omit<DiagnosticEventPayload, "seq" | "ts">) {
const enriched: DiagnosticEventPayload = {
...event,
seq: (seq += 1),
ts: Date.now(),
};
for (const listener of listeners) {
try {
listener(enriched);
} catch {
// Ignore listener failures.
}
}
}
export function onDiagnosticEvent(listener: (evt: DiagnosticEventPayload) => void): () => void {
listeners.add(listener);
return () => listeners.delete(listener);
}
export function resetDiagnosticEventsForTest(): void {
seq = 0;
listeners.clear();
}

View File

@@ -58,7 +58,11 @@ export type {
ChannelToolSend,
} from "../channels/plugins/types.js";
export type { ChannelConfigSchema, ChannelPlugin } from "../channels/plugins/types.plugin.js";
export type { ClawdbotPluginApi } from "../plugins/types.js";
export type {
ClawdbotPluginApi,
ClawdbotPluginService,
ClawdbotPluginServiceContext,
} from "../plugins/types.js";
export type { PluginRuntime } from "../plugins/runtime/types.js";
export { emptyPluginConfigSchema } from "../plugins/config-schema.js";
export type { ClawdbotConfig } from "../config/config.js";
@@ -178,6 +182,12 @@ export { formatDocsLink } from "../terminal/links.js";
export type { HookEntry } from "../hooks/types.js";
export { normalizeE164 } from "../utils.js";
export { missingTargetError } from "../infra/outbound/target-errors.js";
export {
emitDiagnosticEvent,
isDiagnosticsEnabled,
onDiagnosticEvent,
} from "../infra/diagnostic-events.js";
export type { DiagnosticEventPayload, DiagnosticUsageEvent } from "../infra/diagnostic-events.js";
// Channel: Discord
export {