@radaros/observability 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.
package/README.md ADDED
@@ -0,0 +1,42 @@
1
+ # @radaros/observability
2
+
3
+ Opt-in tracing, metrics, and structured logging for RadarOS agents.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install @radaros/observability
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ```typescript
14
+ import { Agent, openai } from "@radaros/core";
15
+ import { instrument } from "@radaros/observability";
16
+
17
+ const agent = new Agent({ name: "assistant", model: openai("gpt-4o") });
18
+
19
+ const obs = instrument(agent, {
20
+ exporters: ["console"], // or "langfuse", "otel", "json-file"
21
+ });
22
+
23
+ await agent.run("Hello!");
24
+ await obs.tracer.flush();
25
+ ```
26
+
27
+ ## Exporters
28
+
29
+ | Shorthand | Env Vars | Description |
30
+ |-----------|----------|-------------|
31
+ | `"console"` | — | Pretty-print trace tree |
32
+ | `"langfuse"` | `LANGFUSE_PUBLIC_KEY`, `LANGFUSE_SECRET_KEY` | Langfuse cloud |
33
+ | `"otel"` | `OTEL_EXPORTER_OTLP_ENDPOINT` | OpenTelemetry collector |
34
+ | `"json-file"` | — | Local JSON file |
35
+
36
+ ## Documentation
37
+
38
+ Full docs at [radaros.mintlify.dev](https://radaros.mintlify.dev)
39
+
40
+ ## License
41
+
42
+ MIT
@@ -0,0 +1,223 @@
1
+ import { EventBus, Agent } from '@radaros/core';
2
+
3
+ type SpanKind = "agent" | "llm" | "tool" | "guardrail" | "memory" | "cache" | "handoff" | "team" | "workflow" | "internal";
4
+ type SpanStatus = "ok" | "error" | "running";
5
+ interface SpanEvent {
6
+ name: string;
7
+ timestamp: number;
8
+ attributes?: Record<string, unknown>;
9
+ }
10
+ interface Span {
11
+ traceId: string;
12
+ spanId: string;
13
+ parentSpanId?: string;
14
+ name: string;
15
+ kind: SpanKind;
16
+ startTime: number;
17
+ endTime?: number;
18
+ durationMs?: number;
19
+ status: SpanStatus;
20
+ attributes: Record<string, unknown>;
21
+ events: SpanEvent[];
22
+ }
23
+ interface Trace {
24
+ traceId: string;
25
+ spans: Span[];
26
+ rootSpanId: string;
27
+ startTime: number;
28
+ endTime?: number;
29
+ durationMs?: number;
30
+ metadata: Record<string, unknown>;
31
+ }
32
+ interface TraceExporter {
33
+ name: string;
34
+ export(trace: Trace): Promise<void>;
35
+ flush?(): Promise<void>;
36
+ shutdown?(): Promise<void>;
37
+ }
38
+ interface MetricsSnapshot {
39
+ counters: {
40
+ runs_total: number;
41
+ runs_success: number;
42
+ runs_error: number;
43
+ tool_calls_total: number;
44
+ handoffs_total: number;
45
+ cache_hits: number;
46
+ cache_misses: number;
47
+ };
48
+ histograms: {
49
+ run_duration_ms: number[];
50
+ tool_latency_ms: number[];
51
+ };
52
+ gauges: {
53
+ total_cost_usd: number;
54
+ total_tokens: number;
55
+ };
56
+ rates: {
57
+ cache_hit_ratio: number;
58
+ error_rate: number;
59
+ };
60
+ timestamp: number;
61
+ }
62
+ type ExporterShorthand = "console" | "langfuse" | "json-file" | "otel";
63
+ interface ObservabilityConfig {
64
+ /** Exporter instances or shorthand strings like "console", "langfuse". */
65
+ exporters?: (TraceExporter | ExporterShorthand)[];
66
+ metrics?: boolean;
67
+ structuredLogs?: boolean | LogDrain;
68
+ }
69
+ type LogDrain = "console" | "json" | ((entry: LogEntry) => void);
70
+ interface LogEntry {
71
+ timestamp: string;
72
+ level: "debug" | "info" | "warn" | "error";
73
+ message: string;
74
+ traceId?: string;
75
+ spanId?: string;
76
+ agentName?: string;
77
+ attributes?: Record<string, unknown>;
78
+ }
79
+
80
+ declare class CallbackExporter implements TraceExporter {
81
+ name: string;
82
+ private callback;
83
+ constructor(callback: (trace: Trace) => void | Promise<void>);
84
+ export(trace: Trace): Promise<void>;
85
+ }
86
+
87
+ declare class ConsoleExporter implements TraceExporter {
88
+ name: string;
89
+ export(trace: Trace): Promise<void>;
90
+ private printSpan;
91
+ }
92
+
93
+ interface JsonFileExporterConfig {
94
+ path?: string;
95
+ mode?: "overwrite" | "append";
96
+ pretty?: boolean;
97
+ }
98
+ declare class JsonFileExporter implements TraceExporter {
99
+ name: string;
100
+ private path;
101
+ private mode;
102
+ private pretty;
103
+ constructor(config?: JsonFileExporterConfig);
104
+ export(trace: Trace): Promise<void>;
105
+ }
106
+
107
+ interface LangfuseExporterConfig {
108
+ /** Defaults to LANGFUSE_PUBLIC_KEY env var. */
109
+ publicKey?: string;
110
+ /** Defaults to LANGFUSE_SECRET_KEY env var. */
111
+ secretKey?: string;
112
+ /** Defaults to LANGFUSE_BASE_URL env var or https://cloud.langfuse.com. */
113
+ baseUrl?: string;
114
+ }
115
+ declare class LangfuseExporter implements TraceExporter {
116
+ name: string;
117
+ private publicKey;
118
+ private secretKey;
119
+ private baseUrl;
120
+ constructor(config?: LangfuseExporterConfig);
121
+ private fetchWithRetry;
122
+ export(trace: Trace): Promise<void>;
123
+ }
124
+
125
+ interface OTelExporterConfig {
126
+ /** Defaults to OTEL_EXPORTER_OTLP_ENDPOINT env var. */
127
+ endpoint?: string;
128
+ /** Defaults to OTEL_EXPORTER_OTLP_HEADERS env var (comma-separated key=value pairs). */
129
+ headers?: Record<string, string>;
130
+ protocol?: "http/json" | "http/protobuf";
131
+ /** Defaults to OTEL_SERVICE_NAME env var or "radaros". */
132
+ serviceName?: string;
133
+ }
134
+ declare class OTelExporter implements TraceExporter {
135
+ name: string;
136
+ private endpoint;
137
+ private headers;
138
+ private serviceName;
139
+ constructor(config?: OTelExporterConfig);
140
+ private fetchWithRetry;
141
+ export(trace: Trace): Promise<void>;
142
+ private padHex;
143
+ private mapKind;
144
+ private toOtlpValue;
145
+ }
146
+
147
+ declare class MetricsCollector {
148
+ private counters;
149
+ private histograms;
150
+ private gauges;
151
+ private readonly MAX_HISTOGRAM_SIZE;
152
+ private toolStartTimes;
153
+ private runStartTimes;
154
+ private listeners;
155
+ attach(eventBus: EventBus): void;
156
+ detach(eventBus: EventBus): void;
157
+ getMetrics(): MetricsSnapshot;
158
+ reset(): void;
159
+ }
160
+
161
+ declare class Tracer {
162
+ private traces;
163
+ private runToTrace;
164
+ private runToRootSpan;
165
+ private activeSpans;
166
+ private exporters;
167
+ private listeners;
168
+ private pendingExports;
169
+ private maxTraces;
170
+ constructor(exporters?: TraceExporter[]);
171
+ attach(eventBus: EventBus): void;
172
+ detach(eventBus: EventBus): void;
173
+ getTrace(traceId: string): Trace | undefined;
174
+ getTraceByRunId(runId: string): Trace | undefined;
175
+ getAllTraces(): Trace[];
176
+ clear(): void;
177
+ flush(): Promise<void>;
178
+ shutdown(): Promise<void>;
179
+ private startSpan;
180
+ private endSpan;
181
+ private addEventToLatestSpan;
182
+ private finalizeTrace;
183
+ private runExporters;
184
+ }
185
+
186
+ declare class StructuredLogger {
187
+ private drain;
188
+ private tracer;
189
+ private listeners;
190
+ constructor(drain?: LogDrain, tracer?: Tracer);
191
+ attach(eventBus: EventBus): void;
192
+ detach(eventBus: EventBus): void;
193
+ private log;
194
+ }
195
+
196
+ interface InstrumentResult {
197
+ tracer: Tracer;
198
+ metrics: MetricsCollector | null;
199
+ logger: StructuredLogger | null;
200
+ detach: () => void;
201
+ }
202
+ /**
203
+ * Attach observability to an agent in one call.
204
+ *
205
+ * ```ts
206
+ * import { instrument } from "@radaros/observability";
207
+ *
208
+ * // Minimal — just pass string shorthands:
209
+ * const obs = instrument(agent, { exporters: ["langfuse", "console"] });
210
+ *
211
+ * // Or bring your own exporter instances for custom config:
212
+ * const obs = instrument(agent, {
213
+ * exporters: [new LangfuseExporter({ baseUrl: "..." }), "console"],
214
+ * });
215
+ * ```
216
+ */
217
+ declare function instrument(agent: Agent, config?: ObservabilityConfig): InstrumentResult;
218
+ /**
219
+ * Attach observability to a raw EventBus (for teams, workflows, or custom setups).
220
+ */
221
+ declare function instrumentBus(eventBus: EventBus, config?: ObservabilityConfig): InstrumentResult;
222
+
223
+ export { CallbackExporter, ConsoleExporter, type ExporterShorthand, type InstrumentResult, JsonFileExporter, type JsonFileExporterConfig, LangfuseExporter, type LangfuseExporterConfig, type LogDrain, type LogEntry, MetricsCollector, type MetricsSnapshot, OTelExporter, type OTelExporterConfig, type ObservabilityConfig, type Span, type SpanEvent, type SpanKind, type SpanStatus, StructuredLogger, type Trace, type TraceExporter, Tracer, instrument, instrumentBus };
package/dist/index.js ADDED
@@ -0,0 +1,1017 @@
1
+ // src/exporters/callback.ts
2
+ var CallbackExporter = class {
3
+ name = "callback";
4
+ callback;
5
+ constructor(callback) {
6
+ this.callback = callback;
7
+ }
8
+ async export(trace) {
9
+ await this.callback(trace);
10
+ }
11
+ };
12
+
13
+ // src/exporters/console.ts
14
+ var C = {
15
+ reset: "\x1B[0m",
16
+ bold: "\x1B[1m",
17
+ dim: "\x1B[2m",
18
+ cyan: "\x1B[36m",
19
+ green: "\x1B[32m",
20
+ red: "\x1B[31m",
21
+ yellow: "\x1B[33m",
22
+ magenta: "\x1B[35m",
23
+ gray: "\x1B[90m",
24
+ brightCyan: "\x1B[96m",
25
+ brightGreen: "\x1B[92m"
26
+ };
27
+ function c(code, text) {
28
+ return `${code}${text}${C.reset}`;
29
+ }
30
+ function statusIcon(status) {
31
+ if (status === "ok") return c(C.green, "\u2713");
32
+ if (status === "error") return c(C.red, "\u2717");
33
+ return c(C.yellow, "\u22EF");
34
+ }
35
+ function kindColor(kind) {
36
+ switch (kind) {
37
+ case "agent":
38
+ return C.brightCyan;
39
+ case "llm":
40
+ return C.yellow;
41
+ case "tool":
42
+ return C.magenta;
43
+ case "handoff":
44
+ return C.brightGreen;
45
+ case "team":
46
+ return C.cyan;
47
+ default:
48
+ return C.gray;
49
+ }
50
+ }
51
+ var ConsoleExporter = class {
52
+ name = "console";
53
+ async export(trace) {
54
+ const line = c(C.dim, "\u2500".repeat(80));
55
+ console.log(`
56
+ ${line}`);
57
+ console.log(
58
+ ` ${c(C.bold + C.brightCyan, "Trace")} ${c(C.dim, trace.traceId)} ${c(C.dim, "duration=")}${c(C.brightGreen, `${trace.durationMs ?? "?"}ms`)}`
59
+ );
60
+ if (trace.metadata.agentName) {
61
+ console.log(` ${c(C.dim, "agent=")}${trace.metadata.agentName}`);
62
+ }
63
+ console.log(line);
64
+ const spanMap = /* @__PURE__ */ new Map();
65
+ for (const span of trace.spans) {
66
+ const parentId = span.parentSpanId ?? "__root__";
67
+ if (!spanMap.has(parentId)) spanMap.set(parentId, []);
68
+ spanMap.get(parentId).push(span);
69
+ }
70
+ const root = trace.spans.find((s) => s.spanId === trace.rootSpanId);
71
+ if (root) {
72
+ this.printSpan(root, spanMap, 0, trace.startTime);
73
+ }
74
+ console.log(line);
75
+ console.log("");
76
+ }
77
+ printSpan(span, childMap, depth, traceStart) {
78
+ const indent = " " + "\u2502 ".repeat(depth);
79
+ const connector = depth > 0 ? "\u251C\u2500 " : "";
80
+ const offset = span.startTime - traceStart;
81
+ const dur = span.durationMs ?? "?";
82
+ const kc = kindColor(span.kind);
83
+ const icon = statusIcon(span.status);
84
+ let line = `${indent}${connector}${icon} ${c(kc, span.name)}`;
85
+ line += ` ${c(C.dim, `[${offset}ms \u2192 +${dur}ms]`)}`;
86
+ if (span.attributes.tokens) {
87
+ line += ` ${c(C.brightGreen, `${span.attributes.tokens} tok`)}`;
88
+ }
89
+ if (span.attributes.toolName) {
90
+ line += ` ${c(C.dim, `(${span.attributes.toolName})`)}`;
91
+ }
92
+ if (span.attributes.cached) {
93
+ line += ` ${c(C.yellow, "[cached]")}`;
94
+ }
95
+ if (span.status === "error" && span.attributes.error) {
96
+ line += ` ${c(C.red, String(span.attributes.error))}`;
97
+ }
98
+ console.log(line);
99
+ for (const evt of span.events) {
100
+ console.log(
101
+ `${indent}\u2502 ${c(C.dim, `\u2937 ${evt.name}`)}${evt.attributes ? c(C.gray, ` ${JSON.stringify(evt.attributes)}`) : ""}`
102
+ );
103
+ }
104
+ const children = childMap.get(span.spanId) ?? [];
105
+ for (const child of children) {
106
+ this.printSpan(child, childMap, depth + 1, traceStart);
107
+ }
108
+ }
109
+ };
110
+
111
+ // src/exporters/json-file.ts
112
+ import { appendFile, writeFile } from "fs/promises";
113
+ var JsonFileExporter = class {
114
+ name = "json-file";
115
+ path;
116
+ mode;
117
+ pretty;
118
+ constructor(config) {
119
+ this.mode = config?.mode ?? "append";
120
+ this.path = config?.path ?? `traces-${Date.now()}.${this.mode === "append" ? "jsonl" : "json"}`;
121
+ this.pretty = config?.pretty ?? true;
122
+ }
123
+ async export(trace) {
124
+ const json = this.pretty ? JSON.stringify(trace, null, 2) : JSON.stringify(trace);
125
+ try {
126
+ if (this.mode === "append") {
127
+ await appendFile(this.path, json + "\n");
128
+ } else {
129
+ await writeFile(this.path, json);
130
+ }
131
+ } catch (err) {
132
+ console.warn(
133
+ `[radaros/observability] Failed to write trace to ${this.path}:`,
134
+ err instanceof Error ? err.message : err
135
+ );
136
+ }
137
+ }
138
+ };
139
+
140
+ // src/exporters/langfuse.ts
141
+ var eventCounter = 0;
142
+ function eventId() {
143
+ return `evt_${Date.now().toString(36)}_${(eventCounter++).toString(36)}`;
144
+ }
145
+ function extractIO(span) {
146
+ const input = span.attributes.input ?? null;
147
+ const output = span.attributes.output ?? null;
148
+ return { input, output };
149
+ }
150
+ var LangfuseExporter = class {
151
+ name = "langfuse";
152
+ publicKey;
153
+ secretKey;
154
+ baseUrl;
155
+ constructor(config) {
156
+ this.publicKey = config?.publicKey ?? process.env.LANGFUSE_PUBLIC_KEY ?? "";
157
+ this.secretKey = config?.secretKey ?? process.env.LANGFUSE_SECRET_KEY ?? "";
158
+ this.baseUrl = (config?.baseUrl ?? process.env.LANGFUSE_BASE_URL ?? "https://cloud.langfuse.com").replace(
159
+ /\/$/,
160
+ ""
161
+ );
162
+ if (!this.publicKey || !this.secretKey) {
163
+ throw new Error(
164
+ "LangfuseExporter: missing credentials. Set LANGFUSE_PUBLIC_KEY and LANGFUSE_SECRET_KEY env vars, or pass them in config."
165
+ );
166
+ }
167
+ }
168
+ async fetchWithRetry(url, init, retries = 2) {
169
+ for (let attempt = 0; attempt <= retries; attempt++) {
170
+ try {
171
+ const controller = new AbortController();
172
+ const timeout = setTimeout(() => controller.abort(), 1e4);
173
+ const res = await fetch(url, { ...init, signal: controller.signal });
174
+ clearTimeout(timeout);
175
+ if (res.status >= 500 && attempt < retries) {
176
+ await new Promise((r) => setTimeout(r, 1e3 * (attempt + 1)));
177
+ continue;
178
+ }
179
+ return res;
180
+ } catch (err) {
181
+ if (attempt === retries) throw err;
182
+ await new Promise((r) => setTimeout(r, 1e3 * (attempt + 1)));
183
+ }
184
+ }
185
+ throw new Error("Unreachable");
186
+ }
187
+ async export(trace) {
188
+ const events = [];
189
+ const now = (/* @__PURE__ */ new Date()).toISOString();
190
+ const rootSpan = trace.spans.find((s) => s.spanId === trace.rootSpanId);
191
+ events.push({
192
+ id: eventId(),
193
+ type: "trace-create",
194
+ timestamp: now,
195
+ body: {
196
+ id: trace.traceId,
197
+ name: String(trace.metadata.agentName ?? "agent.run"),
198
+ input: trace.metadata.input ?? rootSpan?.attributes.input ?? null,
199
+ output: trace.metadata.output ?? rootSpan?.attributes.output ?? null,
200
+ metadata: { ...trace.metadata, input: void 0, output: void 0 },
201
+ timestamp: new Date(trace.startTime).toISOString()
202
+ }
203
+ });
204
+ for (const span of trace.spans) {
205
+ const { input, output } = extractIO(span);
206
+ const { input: _i, output: _o, ...restAttrs } = span.attributes;
207
+ if (span.kind === "llm" || span.name.startsWith("llm.")) {
208
+ events.push({
209
+ id: eventId(),
210
+ type: "generation-create",
211
+ timestamp: now,
212
+ body: {
213
+ id: span.spanId,
214
+ traceId: trace.traceId,
215
+ parentObservationId: span.parentSpanId,
216
+ name: span.name,
217
+ model: span.attributes.modelId ?? void 0,
218
+ input,
219
+ output,
220
+ startTime: new Date(span.startTime).toISOString(),
221
+ endTime: span.endTime ? new Date(span.endTime).toISOString() : void 0,
222
+ usage: {
223
+ promptTokens: span.attributes.promptTokens,
224
+ completionTokens: span.attributes.completionTokens,
225
+ totalTokens: span.attributes.tokens
226
+ },
227
+ metadata: restAttrs
228
+ }
229
+ });
230
+ } else {
231
+ events.push({
232
+ id: eventId(),
233
+ type: "span-create",
234
+ timestamp: now,
235
+ body: {
236
+ id: span.spanId,
237
+ traceId: trace.traceId,
238
+ parentObservationId: span.parentSpanId,
239
+ name: span.name,
240
+ input,
241
+ output,
242
+ startTime: new Date(span.startTime).toISOString(),
243
+ endTime: span.endTime ? new Date(span.endTime).toISOString() : void 0,
244
+ metadata: restAttrs,
245
+ level: span.status === "error" ? "ERROR" : "DEFAULT"
246
+ }
247
+ });
248
+ }
249
+ }
250
+ const auth = Buffer.from(`${this.publicKey}:${this.secretKey}`).toString("base64");
251
+ const res = await this.fetchWithRetry(`${this.baseUrl}/api/public/ingestion`, {
252
+ method: "POST",
253
+ headers: {
254
+ "Content-Type": "application/json",
255
+ Authorization: `Basic ${auth}`
256
+ },
257
+ body: JSON.stringify({ batch: events })
258
+ });
259
+ if (!res.ok && res.status !== 207) {
260
+ throw new Error(`Langfuse export failed: ${res.status} ${res.statusText}`);
261
+ }
262
+ if (res.status === 207) {
263
+ const body = await res.json();
264
+ if (body.errors && body.errors.length > 0) {
265
+ const realErrors = body.errors.filter((e) => e.status >= 400);
266
+ if (realErrors.length > 0) {
267
+ throw new Error(`Langfuse partial failure: ${JSON.stringify(realErrors[0])}`);
268
+ }
269
+ }
270
+ }
271
+ }
272
+ };
273
+
274
+ // src/exporters/otel.ts
275
+ function parseEnvHeaders(raw) {
276
+ if (!raw) return {};
277
+ const result = {};
278
+ for (const pair of raw.split(",")) {
279
+ const [k, ...v] = pair.split("=");
280
+ if (k) result[k.trim()] = v.join("=").trim();
281
+ }
282
+ return result;
283
+ }
284
+ var OTelExporter = class {
285
+ name = "otel";
286
+ endpoint;
287
+ headers;
288
+ serviceName;
289
+ constructor(config) {
290
+ const ep = config?.endpoint ?? process.env.OTEL_EXPORTER_OTLP_ENDPOINT ?? "";
291
+ if (!ep) {
292
+ throw new Error("OTelExporter: missing endpoint. Set OTEL_EXPORTER_OTLP_ENDPOINT env var, or pass it in config.");
293
+ }
294
+ this.endpoint = ep.replace(/\/$/, "");
295
+ this.headers = config?.headers ?? parseEnvHeaders(process.env.OTEL_EXPORTER_OTLP_HEADERS);
296
+ this.serviceName = config?.serviceName ?? process.env.OTEL_SERVICE_NAME ?? "radaros";
297
+ }
298
+ async fetchWithRetry(url, init, retries = 2) {
299
+ for (let attempt = 0; attempt <= retries; attempt++) {
300
+ try {
301
+ const controller = new AbortController();
302
+ const timeout = setTimeout(() => controller.abort(), 1e4);
303
+ const res = await fetch(url, { ...init, signal: controller.signal });
304
+ clearTimeout(timeout);
305
+ if (res.status >= 500 && attempt < retries) {
306
+ await new Promise((r) => setTimeout(r, 1e3 * (attempt + 1)));
307
+ continue;
308
+ }
309
+ return res;
310
+ } catch (err) {
311
+ if (attempt === retries) throw err;
312
+ await new Promise((r) => setTimeout(r, 1e3 * (attempt + 1)));
313
+ }
314
+ }
315
+ throw new Error("Unreachable");
316
+ }
317
+ async export(trace) {
318
+ const otlpSpans = trace.spans.map((span) => ({
319
+ traceId: this.padHex(span.traceId, 32),
320
+ spanId: this.padHex(span.spanId, 16),
321
+ parentSpanId: span.parentSpanId ? this.padHex(span.parentSpanId, 16) : void 0,
322
+ name: span.name,
323
+ kind: this.mapKind(span.kind),
324
+ startTimeUnixNano: String(span.startTime * 1e6),
325
+ endTimeUnixNano: span.endTime ? String(span.endTime * 1e6) : void 0,
326
+ status: {
327
+ code: span.status === "error" ? 2 : 1,
328
+ message: span.status === "error" ? String(span.attributes.error ?? "") : void 0
329
+ },
330
+ attributes: Object.entries(span.attributes).map(([key, value]) => ({
331
+ key,
332
+ value: this.toOtlpValue(value)
333
+ })),
334
+ events: span.events.map((evt) => ({
335
+ name: evt.name,
336
+ timeUnixNano: String(evt.timestamp * 1e6),
337
+ attributes: evt.attributes ? Object.entries(evt.attributes).map(([k, v]) => ({ key: k, value: this.toOtlpValue(v) })) : []
338
+ }))
339
+ }));
340
+ const payload = {
341
+ resourceSpans: [
342
+ {
343
+ resource: {
344
+ attributes: [{ key: "service.name", value: { stringValue: this.serviceName } }]
345
+ },
346
+ scopeSpans: [
347
+ {
348
+ scope: { name: "@radaros/observability" },
349
+ spans: otlpSpans
350
+ }
351
+ ]
352
+ }
353
+ ]
354
+ };
355
+ const url = `${this.endpoint}/v1/traces`;
356
+ const res = await this.fetchWithRetry(url, {
357
+ method: "POST",
358
+ headers: {
359
+ "Content-Type": "application/json",
360
+ ...this.headers
361
+ },
362
+ body: JSON.stringify(payload)
363
+ });
364
+ if (!res.ok) {
365
+ throw new Error(`OTLP export failed: ${res.status} ${res.statusText}`);
366
+ }
367
+ }
368
+ padHex(id, length) {
369
+ let hex = "";
370
+ for (let i = 0; i < id.length; i++) {
371
+ hex += id.charCodeAt(i).toString(16).padStart(2, "0");
372
+ }
373
+ return hex.padStart(length, "0").slice(-length);
374
+ }
375
+ mapKind(kind) {
376
+ switch (kind) {
377
+ case "agent":
378
+ return 1;
379
+ case "llm":
380
+ return 3;
381
+ case "tool":
382
+ return 3;
383
+ default:
384
+ return 0;
385
+ }
386
+ }
387
+ toOtlpValue(value) {
388
+ if (typeof value === "string") return { stringValue: value };
389
+ if (typeof value === "number")
390
+ return Number.isInteger(value) ? { intValue: String(value) } : { doubleValue: value };
391
+ if (typeof value === "boolean") return { boolValue: value };
392
+ if (Array.isArray(value)) return { stringValue: JSON.stringify(value) };
393
+ return { stringValue: String(value) };
394
+ }
395
+ };
396
+
397
+ // src/metrics.ts
398
+ var MetricsCollector = class {
399
+ counters = {
400
+ runs_total: 0,
401
+ runs_success: 0,
402
+ runs_error: 0,
403
+ tool_calls_total: 0,
404
+ handoffs_total: 0,
405
+ cache_hits: 0,
406
+ cache_misses: 0
407
+ };
408
+ histograms = {
409
+ run_duration_ms: [],
410
+ tool_latency_ms: []
411
+ };
412
+ gauges = {
413
+ total_cost_usd: 0,
414
+ total_tokens: 0
415
+ };
416
+ MAX_HISTOGRAM_SIZE = 1e4;
417
+ toolStartTimes = /* @__PURE__ */ new Map();
418
+ runStartTimes = /* @__PURE__ */ new Map();
419
+ listeners = [];
420
+ attach(eventBus) {
421
+ const on = (event, handler) => {
422
+ eventBus.on(event, handler);
423
+ this.listeners.push({ event, handler });
424
+ };
425
+ on("run.start", (data) => {
426
+ this.counters.runs_total++;
427
+ this.runStartTimes.set(data.runId, Date.now());
428
+ });
429
+ on("run.complete", (data) => {
430
+ this.counters.runs_success++;
431
+ const startTime = this.runStartTimes.get(data.runId);
432
+ if (startTime) {
433
+ const duration = Date.now() - startTime;
434
+ this.histograms.run_duration_ms.push(duration);
435
+ if (this.histograms.run_duration_ms.length > this.MAX_HISTOGRAM_SIZE) {
436
+ this.histograms.run_duration_ms = this.histograms.run_duration_ms.slice(-this.MAX_HISTOGRAM_SIZE);
437
+ }
438
+ this.runStartTimes.delete(data.runId);
439
+ }
440
+ if (data.output?.usage?.totalTokens) {
441
+ this.gauges.total_tokens += data.output.usage.totalTokens;
442
+ }
443
+ for (const key of this.toolStartTimes.keys()) {
444
+ if (key.startsWith(`${data.runId}:`)) this.toolStartTimes.delete(key);
445
+ }
446
+ });
447
+ on("run.error", (data) => {
448
+ this.counters.runs_error++;
449
+ const startTime = this.runStartTimes.get(data.runId);
450
+ if (startTime) {
451
+ const duration = Date.now() - startTime;
452
+ this.histograms.run_duration_ms.push(duration);
453
+ if (this.histograms.run_duration_ms.length > this.MAX_HISTOGRAM_SIZE) {
454
+ this.histograms.run_duration_ms = this.histograms.run_duration_ms.slice(-this.MAX_HISTOGRAM_SIZE);
455
+ }
456
+ this.runStartTimes.delete(data.runId);
457
+ }
458
+ for (const key of this.toolStartTimes.keys()) {
459
+ if (key.startsWith(`${data.runId}:`)) this.toolStartTimes.delete(key);
460
+ }
461
+ });
462
+ on("tool.call", (data) => {
463
+ this.counters.tool_calls_total++;
464
+ this.toolStartTimes.set(`${data.runId}:${data.toolName}`, Date.now());
465
+ });
466
+ on("tool.result", (data) => {
467
+ const key = `${data.runId}:${data.toolName}`;
468
+ const start = this.toolStartTimes.get(key);
469
+ if (start) {
470
+ this.histograms.tool_latency_ms.push(Date.now() - start);
471
+ if (this.histograms.tool_latency_ms.length > this.MAX_HISTOGRAM_SIZE) {
472
+ this.histograms.tool_latency_ms = this.histograms.tool_latency_ms.slice(-this.MAX_HISTOGRAM_SIZE);
473
+ }
474
+ this.toolStartTimes.delete(key);
475
+ }
476
+ });
477
+ on("handoff.transfer", () => {
478
+ this.counters.handoffs_total++;
479
+ });
480
+ on("cache.hit", () => {
481
+ this.counters.cache_hits++;
482
+ });
483
+ on("cache.miss", () => {
484
+ this.counters.cache_misses++;
485
+ });
486
+ on("cost.tracked", (data) => {
487
+ if (data.usage?.totalTokens) {
488
+ this.gauges.total_tokens += data.usage.totalTokens;
489
+ }
490
+ if (data.usage?.cost) {
491
+ this.gauges.total_cost_usd += data.usage.cost;
492
+ }
493
+ });
494
+ }
495
+ detach(eventBus) {
496
+ for (const { event, handler } of this.listeners) {
497
+ eventBus.off(event, handler);
498
+ }
499
+ this.listeners = [];
500
+ }
501
+ getMetrics() {
502
+ const totalCacheAttempts = this.counters.cache_hits + this.counters.cache_misses;
503
+ return {
504
+ counters: { ...this.counters },
505
+ histograms: {
506
+ run_duration_ms: [...this.histograms.run_duration_ms],
507
+ tool_latency_ms: [...this.histograms.tool_latency_ms]
508
+ },
509
+ gauges: { ...this.gauges },
510
+ rates: {
511
+ cache_hit_ratio: totalCacheAttempts > 0 ? this.counters.cache_hits / totalCacheAttempts : 0,
512
+ error_rate: this.counters.runs_total > 0 ? this.counters.runs_error / this.counters.runs_total : 0
513
+ },
514
+ timestamp: Date.now()
515
+ };
516
+ }
517
+ reset() {
518
+ this.counters = {
519
+ runs_total: 0,
520
+ runs_success: 0,
521
+ runs_error: 0,
522
+ tool_calls_total: 0,
523
+ handoffs_total: 0,
524
+ cache_hits: 0,
525
+ cache_misses: 0
526
+ };
527
+ this.histograms = {
528
+ run_duration_ms: [],
529
+ tool_latency_ms: []
530
+ };
531
+ this.gauges = {
532
+ total_cost_usd: 0,
533
+ total_tokens: 0
534
+ };
535
+ this.toolStartTimes.clear();
536
+ this.runStartTimes.clear();
537
+ }
538
+ };
539
+
540
+ // src/structured-logger.ts
541
+ var StructuredLogger = class {
542
+ drain;
543
+ tracer;
544
+ listeners = [];
545
+ constructor(drain = "json", tracer) {
546
+ this.drain = drain;
547
+ this.tracer = tracer ?? null;
548
+ }
549
+ attach(eventBus) {
550
+ const on = (event, handler) => {
551
+ eventBus.on(event, handler);
552
+ this.listeners.push({ event, handler });
553
+ };
554
+ on("run.start", (data) => {
555
+ this.log(
556
+ "info",
557
+ "Run started",
558
+ data.agentName,
559
+ {
560
+ runId: data.runId,
561
+ input: data.input?.slice(0, 200) ?? ""
562
+ },
563
+ data.runId
564
+ );
565
+ });
566
+ on("run.complete", (data) => {
567
+ this.log(
568
+ "info",
569
+ "Run completed",
570
+ data.output?.agentName ?? void 0,
571
+ {
572
+ runId: data.runId,
573
+ tokens: data.output?.usage?.totalTokens,
574
+ durationMs: data.output?.durationMs
575
+ },
576
+ data.runId
577
+ );
578
+ });
579
+ on("run.error", (data) => {
580
+ this.log(
581
+ "error",
582
+ `Run failed: ${data.error?.message ?? "unknown error"}`,
583
+ void 0,
584
+ {
585
+ runId: data.runId,
586
+ error: data.error?.message ?? "unknown error",
587
+ stack: data.error?.stack?.split("\n").slice(0, 3).join("\n")
588
+ },
589
+ data.runId
590
+ );
591
+ });
592
+ on("tool.call", (data) => {
593
+ this.log(
594
+ "debug",
595
+ `Tool call: ${data.toolName}`,
596
+ void 0,
597
+ {
598
+ runId: data.runId,
599
+ toolName: data.toolName
600
+ },
601
+ data.runId
602
+ );
603
+ });
604
+ on("tool.result", (data) => {
605
+ this.log(
606
+ "debug",
607
+ `Tool result: ${data.toolName}`,
608
+ void 0,
609
+ {
610
+ runId: data.runId,
611
+ toolName: data.toolName
612
+ },
613
+ data.runId
614
+ );
615
+ });
616
+ on("handoff.transfer", (data) => {
617
+ this.log(
618
+ "info",
619
+ `Handoff: ${data.fromAgent} -> ${data.toAgent}`,
620
+ data.fromAgent,
621
+ {
622
+ runId: data.runId,
623
+ toAgent: data.toAgent,
624
+ reason: data.reason
625
+ },
626
+ data.runId
627
+ );
628
+ });
629
+ on("cache.hit", (data) => {
630
+ this.log("debug", "Cache hit", data.agentName, {
631
+ input: data.input?.slice(0, 100) ?? ""
632
+ });
633
+ });
634
+ on("cache.miss", (data) => {
635
+ this.log("debug", "Cache miss", data.agentName, {
636
+ input: data.input?.slice(0, 100) ?? ""
637
+ });
638
+ });
639
+ on("cost.tracked", (data) => {
640
+ this.log(
641
+ "info",
642
+ `Cost tracked: ${data.modelId}`,
643
+ data.agentName,
644
+ {
645
+ runId: data.runId,
646
+ modelId: data.modelId,
647
+ tokens: data.usage?.totalTokens
648
+ },
649
+ data.runId
650
+ );
651
+ });
652
+ }
653
+ detach(eventBus) {
654
+ for (const { event, handler } of this.listeners) {
655
+ eventBus.off(event, handler);
656
+ }
657
+ this.listeners = [];
658
+ }
659
+ log(level, message, agentName, attributes, runId) {
660
+ const traceId = runId && this.tracer ? this.tracer.getTraceByRunId(runId)?.traceId : void 0;
661
+ const entry = {
662
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
663
+ level,
664
+ message,
665
+ traceId,
666
+ agentName,
667
+ attributes
668
+ };
669
+ if (typeof this.drain === "function") {
670
+ this.drain(entry);
671
+ } else if (this.drain === "json") {
672
+ console.log(JSON.stringify(entry));
673
+ } else {
674
+ console.log(`[${entry.timestamp}] ${level.toUpperCase()} ${message}${traceId ? ` trace=${traceId}` : ""}`);
675
+ }
676
+ }
677
+ };
678
+
679
+ // src/tracer.ts
680
+ var idCounter = 0;
681
+ function genId() {
682
+ return `${Date.now().toString(36)}_${(idCounter++).toString(36)}_${Math.random().toString(36).slice(2, 6)}`;
683
+ }
684
+ var Tracer = class {
685
+ traces = /* @__PURE__ */ new Map();
686
+ runToTrace = /* @__PURE__ */ new Map();
687
+ runToRootSpan = /* @__PURE__ */ new Map();
688
+ activeSpans = /* @__PURE__ */ new Map();
689
+ exporters;
690
+ listeners = [];
691
+ pendingExports = [];
692
+ maxTraces = 1e3;
693
+ constructor(exporters = []) {
694
+ this.exporters = exporters;
695
+ }
696
+ attach(eventBus) {
697
+ const on = (event, handler) => {
698
+ eventBus.on(event, handler);
699
+ this.listeners.push({ event, handler });
700
+ };
701
+ on("run.start", (data) => {
702
+ const traceId = genId();
703
+ const inputText = data.input?.slice(0, 1e3) ?? "";
704
+ const span = this.startSpan(traceId, "agent.run", "agent", {
705
+ agentName: data.agentName,
706
+ input: inputText,
707
+ runId: data.runId
708
+ });
709
+ this.runToTrace.set(data.runId, traceId);
710
+ this.runToRootSpan.set(data.runId, span.spanId);
711
+ const trace = {
712
+ traceId,
713
+ spans: [span],
714
+ rootSpanId: span.spanId,
715
+ startTime: span.startTime,
716
+ metadata: { agentName: data.agentName, runId: data.runId, input: inputText }
717
+ };
718
+ this.traces.set(traceId, trace);
719
+ this.activeSpans.set(`${data.runId}:root`, span);
720
+ });
721
+ on("run.complete", (data) => {
722
+ const span = this.activeSpans.get(`${data.runId}:root`);
723
+ if (span) {
724
+ const outputText = data.output?.text?.slice(0, 2e3) ?? "";
725
+ span.attributes.output = outputText;
726
+ span.attributes.outputLength = data.output?.text?.length ?? 0;
727
+ span.attributes.tokens = data.output?.usage?.totalTokens ?? 0;
728
+ span.attributes.promptTokens = data.output?.usage?.promptTokens ?? 0;
729
+ span.attributes.completionTokens = data.output?.usage?.completionTokens ?? 0;
730
+ this.endSpan(span, "ok");
731
+ this.activeSpans.delete(`${data.runId}:root`);
732
+ const traceId = this.runToTrace.get(data.runId);
733
+ const trace = traceId ? this.traces.get(traceId) : void 0;
734
+ if (trace) {
735
+ trace.metadata.output = outputText;
736
+ }
737
+ this.finalizeTrace(data.runId);
738
+ }
739
+ });
740
+ on("run.error", (data) => {
741
+ const span = this.activeSpans.get(`${data.runId}:root`);
742
+ if (span) {
743
+ span.attributes.error = data.error.message;
744
+ this.endSpan(span, "error");
745
+ this.activeSpans.delete(`${data.runId}:root`);
746
+ this.finalizeTrace(data.runId);
747
+ }
748
+ });
749
+ on("tool.call", (data) => {
750
+ const traceId = this.runToTrace.get(data.runId);
751
+ const parentId = this.runToRootSpan.get(data.runId);
752
+ if (!traceId) return;
753
+ const argsStr = typeof data.args === "string" ? data.args : JSON.stringify(data.args ?? {});
754
+ const span = this.startSpan(
755
+ traceId,
756
+ `tool.${data.toolName}`,
757
+ "tool",
758
+ {
759
+ toolName: data.toolName,
760
+ runId: data.runId,
761
+ input: argsStr.slice(0, 1e3)
762
+ },
763
+ parentId
764
+ );
765
+ const trace = this.traces.get(traceId);
766
+ trace?.spans.push(span);
767
+ this.activeSpans.set(`${data.runId}:tool:${data.toolName}:${span.spanId}`, span);
768
+ });
769
+ on("tool.result", (data) => {
770
+ const prefix = `${data.runId}:tool:${data.toolName}:`;
771
+ let matchKey;
772
+ for (const key of this.activeSpans.keys()) {
773
+ if (key.startsWith(prefix)) {
774
+ matchKey = key;
775
+ break;
776
+ }
777
+ }
778
+ if (!matchKey) return;
779
+ const span = this.activeSpans.get(matchKey);
780
+ const resultStr = typeof data.result === "string" ? data.result : JSON.stringify(data.result ?? "");
781
+ span.attributes.output = resultStr.slice(0, 2e3);
782
+ span.attributes.resultLength = resultStr.length;
783
+ span.attributes.cached = resultStr.startsWith("[cached]");
784
+ this.endSpan(span, "ok");
785
+ this.activeSpans.delete(matchKey);
786
+ });
787
+ on("handoff.transfer", (data) => {
788
+ const traceId = this.runToTrace.get(data.runId);
789
+ const parentId = this.runToRootSpan.get(data.runId);
790
+ if (!traceId) return;
791
+ const span = this.startSpan(
792
+ traceId,
793
+ `handoff.${data.fromAgent}->${data.toAgent}`,
794
+ "handoff",
795
+ {
796
+ fromAgent: data.fromAgent,
797
+ toAgent: data.toAgent,
798
+ reason: data.reason
799
+ },
800
+ parentId
801
+ );
802
+ this.endSpan(span, "ok");
803
+ const trace = this.traces.get(traceId);
804
+ trace?.spans.push(span);
805
+ });
806
+ on("handoff.complete", (data) => {
807
+ const span = this.activeSpans.get(`${data.runId}:root`);
808
+ if (span) {
809
+ span.attributes.handoffChain = data.chain;
810
+ span.attributes.finalAgent = data.finalAgent;
811
+ }
812
+ });
813
+ on("team.delegate", (data) => {
814
+ const traceId = this.runToTrace.get(data.runId);
815
+ const parentId = this.runToRootSpan.get(data.runId);
816
+ if (!traceId) return;
817
+ const span = this.startSpan(
818
+ traceId,
819
+ `team.delegate.${data.memberId}`,
820
+ "team",
821
+ {
822
+ memberId: data.memberId,
823
+ task: data.task.slice(0, 200)
824
+ },
825
+ parentId
826
+ );
827
+ this.endSpan(span, "ok");
828
+ const trace = this.traces.get(traceId);
829
+ trace?.spans.push(span);
830
+ });
831
+ on("cache.hit", (data) => {
832
+ for (const [key, span] of this.activeSpans) {
833
+ if (key.endsWith(":root") && span.attributes.agentName === data.agentName) {
834
+ span.events.push({ name: "cache.hit", timestamp: Date.now(), attributes: { cachedId: data.cachedId } });
835
+ break;
836
+ }
837
+ }
838
+ });
839
+ on("cache.miss", (data) => {
840
+ for (const [key, span] of this.activeSpans) {
841
+ if (key.endsWith(":root") && span.attributes.agentName === data.agentName) {
842
+ span.events.push({ name: "cache.miss", timestamp: Date.now(), attributes: {} });
843
+ break;
844
+ }
845
+ }
846
+ });
847
+ on("cost.tracked", (data) => {
848
+ const span = this.activeSpans.get(`${data.runId}:root`);
849
+ if (span) {
850
+ span.attributes.modelId = data.modelId;
851
+ }
852
+ });
853
+ on("memory.extract", (data) => {
854
+ for (const [key, span] of this.activeSpans) {
855
+ if (key.endsWith(":root") && span.attributes.agentName === data.agentName) {
856
+ span.events.push({
857
+ name: "memory.extract",
858
+ timestamp: Date.now(),
859
+ attributes: { sessionId: data.sessionId, agentName: data.agentName }
860
+ });
861
+ break;
862
+ }
863
+ }
864
+ });
865
+ }
866
+ detach(eventBus) {
867
+ for (const { event, handler } of this.listeners) {
868
+ eventBus.off(event, handler);
869
+ }
870
+ this.listeners = [];
871
+ }
872
+ getTrace(traceId) {
873
+ return this.traces.get(traceId);
874
+ }
875
+ getTraceByRunId(runId) {
876
+ const traceId = this.runToTrace.get(runId);
877
+ return traceId ? this.traces.get(traceId) : void 0;
878
+ }
879
+ getAllTraces() {
880
+ return [...this.traces.values()];
881
+ }
882
+ clear() {
883
+ this.traces.clear();
884
+ this.runToTrace.clear();
885
+ this.runToRootSpan.clear();
886
+ this.activeSpans.clear();
887
+ }
888
+ async flush() {
889
+ await Promise.allSettled(this.pendingExports);
890
+ this.pendingExports = [];
891
+ for (const exporter of this.exporters) {
892
+ if (exporter.flush) await exporter.flush();
893
+ }
894
+ }
895
+ async shutdown() {
896
+ await this.flush();
897
+ for (const exporter of this.exporters) {
898
+ if (exporter.shutdown) await exporter.shutdown();
899
+ }
900
+ }
901
+ startSpan(traceId, name, kind, attributes, parentSpanId) {
902
+ return {
903
+ traceId,
904
+ spanId: genId(),
905
+ parentSpanId,
906
+ name,
907
+ kind,
908
+ startTime: Date.now(),
909
+ status: "running",
910
+ attributes,
911
+ events: []
912
+ };
913
+ }
914
+ endSpan(span, status) {
915
+ span.endTime = Date.now();
916
+ span.durationMs = span.endTime - span.startTime;
917
+ span.status = status;
918
+ }
919
+ addEventToLatestSpan(runId, name, attributes) {
920
+ const span = this.activeSpans.get(`${runId}:root`);
921
+ if (span) {
922
+ span.events.push({ name, timestamp: Date.now(), attributes });
923
+ }
924
+ }
925
+ finalizeTrace(runId) {
926
+ const traceId = this.runToTrace.get(runId);
927
+ if (!traceId) return;
928
+ const trace = this.traces.get(traceId);
929
+ if (!trace) return;
930
+ const root = trace.spans.find((s) => s.spanId === trace.rootSpanId);
931
+ if (root) {
932
+ trace.endTime = root.endTime;
933
+ trace.durationMs = root.durationMs;
934
+ }
935
+ const exportPromise = this.runExporters(trace);
936
+ this.pendingExports.push(exportPromise);
937
+ exportPromise.finally(() => {
938
+ const idx = this.pendingExports.indexOf(exportPromise);
939
+ if (idx >= 0) this.pendingExports.splice(idx, 1);
940
+ });
941
+ this.runToTrace.delete(runId);
942
+ this.runToRootSpan.delete(runId);
943
+ if (this.traces.size > this.maxTraces) {
944
+ const oldest = this.traces.keys().next().value;
945
+ if (oldest) this.traces.delete(oldest);
946
+ }
947
+ }
948
+ async runExporters(trace) {
949
+ for (const exporter of this.exporters) {
950
+ try {
951
+ await exporter.export(trace);
952
+ } catch (err) {
953
+ console.warn(
954
+ `[radaros/observability] Export failed (${exporter.name}):`,
955
+ err instanceof Error ? err.message : err
956
+ );
957
+ }
958
+ }
959
+ }
960
+ };
961
+
962
+ // src/instrument.ts
963
+ function resolveExporters(raw) {
964
+ if (!raw || raw.length === 0) return [];
965
+ return raw.map((e) => {
966
+ if (typeof e !== "string") return e;
967
+ const shorthand = {
968
+ console: () => new ConsoleExporter(),
969
+ langfuse: () => new LangfuseExporter(),
970
+ "json-file": () => new JsonFileExporter(),
971
+ otel: () => new OTelExporter()
972
+ };
973
+ const factory = shorthand[e];
974
+ if (!factory) throw new Error(`Unknown exporter: "${e}". Use "console", "langfuse", "json-file", or "otel".`);
975
+ return factory();
976
+ });
977
+ }
978
+ function buildResult(eventBus, config) {
979
+ const exporters = resolveExporters(config?.exporters);
980
+ const tracer = new Tracer(exporters);
981
+ tracer.attach(eventBus);
982
+ let metrics = null;
983
+ if (config?.metrics !== false) {
984
+ metrics = new MetricsCollector();
985
+ metrics.attach(eventBus);
986
+ }
987
+ let logger = null;
988
+ if (config?.structuredLogs) {
989
+ const drain = config.structuredLogs === true ? "json" : config.structuredLogs;
990
+ logger = new StructuredLogger(drain, tracer);
991
+ logger.attach(eventBus);
992
+ }
993
+ const detach = () => {
994
+ tracer.detach(eventBus);
995
+ metrics?.detach(eventBus);
996
+ logger?.detach(eventBus);
997
+ };
998
+ return { tracer, metrics, logger, detach };
999
+ }
1000
+ function instrument(agent, config) {
1001
+ return buildResult(agent.eventBus, config);
1002
+ }
1003
+ function instrumentBus(eventBus, config) {
1004
+ return buildResult(eventBus, config);
1005
+ }
1006
+ export {
1007
+ CallbackExporter,
1008
+ ConsoleExporter,
1009
+ JsonFileExporter,
1010
+ LangfuseExporter,
1011
+ MetricsCollector,
1012
+ OTelExporter,
1013
+ StructuredLogger,
1014
+ Tracer,
1015
+ instrument,
1016
+ instrumentBus
1017
+ };
package/package.json ADDED
@@ -0,0 +1,45 @@
1
+ {
2
+ "name": "@radaros/observability",
3
+ "version": "0.1.0",
4
+ "description": "Tracing, metrics, and structured logging for RadarOS agents",
5
+ "license": "MIT",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "https://github.com/xhipment/agentOs.git",
9
+ "directory": "packages/observability"
10
+ },
11
+ "keywords": [
12
+ "ai",
13
+ "agents",
14
+ "observability",
15
+ "tracing",
16
+ "metrics",
17
+ "langfuse",
18
+ "opentelemetry"
19
+ ],
20
+ "type": "module",
21
+ "main": "./dist/index.js",
22
+ "types": "./dist/index.d.ts",
23
+ "exports": {
24
+ ".": {
25
+ "import": "./dist/index.js",
26
+ "types": "./dist/index.d.ts"
27
+ }
28
+ },
29
+ "files": [
30
+ "dist"
31
+ ],
32
+ "scripts": {
33
+ "build": "tsup src/index.ts --format esm --dts --clean",
34
+ "dev": "tsup src/index.ts --format esm --dts --watch",
35
+ "prepublishOnly": "npm run build"
36
+ },
37
+ "peerDependencies": {
38
+ "@radaros/core": "^0.3.10"
39
+ },
40
+ "devDependencies": {
41
+ "@types/node": "^25.3.1",
42
+ "tsup": "^8.0.0",
43
+ "typescript": "^5.6.0"
44
+ }
45
+ }