intentkit-otel 1.0.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,93 @@
1
+ # intentkit-otel
2
+
3
+ OpenTelemetry middleware for IntentKit. Exports function call telemetry as OTLP spans to any compatible backend (DataDog, Grafana Cloud, New Relic, Jaeger, etc.).
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install intentkit-otel
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```typescript
14
+ import { IntentRegistry, serve } from 'intentkit';
15
+ import { createOtelMiddleware } from 'intentkit-otel';
16
+
17
+ const { middleware, shutdown } = createOtelMiddleware({
18
+ endpoint: 'http://localhost:4318/v1/traces',
19
+ serviceName: 'my-agent-service',
20
+ });
21
+
22
+ const registry = new IntentRegistry()
23
+ .use(middleware)
24
+ .register(/* your functions */);
25
+
26
+ await serve({ registry, context });
27
+
28
+ // On process exit:
29
+ process.on('SIGTERM', async () => {
30
+ await shutdown();
31
+ process.exit(0);
32
+ });
33
+ ```
34
+
35
+ ## Span Attributes
36
+
37
+ Every function call produces a span with:
38
+
39
+ | Attribute | Description |
40
+ |---|---|
41
+ | `intentkit.function.name` | Function name (e.g., `create_task`) |
42
+ | `intentkit.caller.role` | Caller's role (e.g., `admin`) |
43
+ | `intentkit.caller.capabilities` | Comma-separated capabilities |
44
+ | `intentkit.request.id` | Unique request UUID |
45
+ | `intentkit.status` | `success` or `error` |
46
+ | `intentkit.duration_ms` | Execution time in milliseconds |
47
+ | `intentkit.events.count` | Number of events emitted |
48
+ | `intentkit.events.names` | Comma-separated event names |
49
+
50
+ ## Backend Configuration
51
+
52
+ ### DataDog
53
+
54
+ ```typescript
55
+ createOtelMiddleware({
56
+ endpoint: 'https://trace.agent.datadoghq.com/v1/traces',
57
+ serviceName: 'my-service',
58
+ headers: { 'DD-API-KEY': process.env.DD_API_KEY! },
59
+ });
60
+ ```
61
+
62
+ ### Grafana Cloud
63
+
64
+ ```typescript
65
+ createOtelMiddleware({
66
+ endpoint: 'https://otlp-gateway-prod-us-central-0.grafana.net/otlp/v1/traces',
67
+ serviceName: 'my-service',
68
+ headers: { Authorization: `Basic ${btoa(`${instanceId}:${apiKey}`)}` },
69
+ });
70
+ ```
71
+
72
+ ### New Relic
73
+
74
+ ```typescript
75
+ createOtelMiddleware({
76
+ endpoint: 'https://otlp.nr-data.net/v1/traces',
77
+ serviceName: 'my-service',
78
+ headers: { 'api-key': process.env.NEW_RELIC_LICENSE_KEY! },
79
+ });
80
+ ```
81
+
82
+ ### Local (Jaeger)
83
+
84
+ ```typescript
85
+ createOtelMiddleware({
86
+ endpoint: 'http://localhost:4318/v1/traces',
87
+ serviceName: 'my-service',
88
+ });
89
+ ```
90
+
91
+ ## License
92
+
93
+ MIT
@@ -0,0 +1,3 @@
1
+ export { createOtelMiddleware } from './middleware.js';
2
+ export type { OtelOptions } from './types.js';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AACvD,YAAY,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { createOtelMiddleware } from './middleware.js';
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC"}
@@ -0,0 +1,18 @@
1
+ import type { Middleware } from 'intentkit';
2
+ import type { OtelOptions } from './types.js';
3
+ /**
4
+ * Create an OpenTelemetry tracing middleware for IntentKit.
5
+ *
6
+ * Wraps every function call in an OTLP span with attributes for
7
+ * function name, caller role, capabilities, request ID, duration,
8
+ * and emitted events. Exports spans to any OTLP-compatible backend
9
+ * (DataDog, Grafana Cloud, New Relic, Jaeger, etc.).
10
+ *
11
+ * @returns middleware to register via `registry.use()` and a shutdown
12
+ * function to flush pending spans on process exit.
13
+ */
14
+ export declare function createOtelMiddleware(options: OtelOptions): {
15
+ middleware: Middleware;
16
+ shutdown: () => Promise<void>;
17
+ };
18
+ //# sourceMappingURL=middleware.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../../src/middleware.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,UAAU,EAAqC,MAAM,WAAW,CAAC;AAC/E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C;;;;;;;;;;GAUG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,WAAW,GAAG;IAC1D,UAAU,EAAE,UAAU,CAAC;IACvB,QAAQ,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC/B,CA6FA"}
@@ -0,0 +1,87 @@
1
+ import { trace, SpanStatusCode } from '@opentelemetry/api';
2
+ import { BasicTracerProvider, SimpleSpanProcessor } from '@opentelemetry/sdk-trace-base';
3
+ import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';
4
+ import { Resource } from '@opentelemetry/resources';
5
+ /**
6
+ * Create an OpenTelemetry tracing middleware for IntentKit.
7
+ *
8
+ * Wraps every function call in an OTLP span with attributes for
9
+ * function name, caller role, capabilities, request ID, duration,
10
+ * and emitted events. Exports spans to any OTLP-compatible backend
11
+ * (DataDog, Grafana Cloud, New Relic, Jaeger, etc.).
12
+ *
13
+ * @returns middleware to register via `registry.use()` and a shutdown
14
+ * function to flush pending spans on process exit.
15
+ */
16
+ export function createOtelMiddleware(options) {
17
+ // --- Setup exporter + provider ---
18
+ const exporter = new OTLPTraceExporter({
19
+ url: options.endpoint,
20
+ headers: options.headers,
21
+ });
22
+ const resourceAttributes = {
23
+ 'service.name': options.serviceName,
24
+ ...options.attributes,
25
+ };
26
+ const provider = new BasicTracerProvider({
27
+ resource: new Resource(resourceAttributes),
28
+ });
29
+ provider.addSpanProcessor(new SimpleSpanProcessor(exporter));
30
+ provider.register();
31
+ const tracer = trace.getTracer('intentkit', '1.0.0');
32
+ // --- Build middleware ---
33
+ const middleware = {
34
+ name: 'intentkit-otel',
35
+ execute: async (fn, input, ctx, next) => {
36
+ const span = tracer.startSpan(fn.name);
37
+ // Set base attributes
38
+ span.setAttribute('intentkit.function.name', fn.name);
39
+ span.setAttribute('intentkit.caller.role', ctx.caller.role);
40
+ span.setAttribute('intentkit.caller.capabilities', ctx.caller.capabilities.join(','));
41
+ span.setAttribute('intentkit.request.id', ctx.requestId);
42
+ // Intercept ctx.emit to track emitted events
43
+ const emittedEvents = [];
44
+ const originalEmit = ctx.emit;
45
+ ctx.emit = (eventName, data) => {
46
+ emittedEvents.push(eventName);
47
+ return originalEmit.call(ctx, eventName, data);
48
+ };
49
+ const startTime = Date.now();
50
+ try {
51
+ const result = await next();
52
+ // Success attributes
53
+ span.setAttribute('intentkit.status', 'success');
54
+ span.setAttribute('intentkit.duration_ms', Date.now() - startTime);
55
+ span.setAttribute('intentkit.events.count', emittedEvents.length);
56
+ span.setAttribute('intentkit.events.names', emittedEvents.join(','));
57
+ span.setStatus({ code: SpanStatusCode.OK });
58
+ span.end();
59
+ // Restore original emit
60
+ ctx.emit = originalEmit;
61
+ return result;
62
+ }
63
+ catch (error) {
64
+ const message = error instanceof Error ? error.message : String(error);
65
+ // Error attributes
66
+ span.setAttribute('intentkit.status', 'error');
67
+ span.setAttribute('intentkit.duration_ms', Date.now() - startTime);
68
+ span.setAttribute('intentkit.events.count', emittedEvents.length);
69
+ span.setAttribute('intentkit.events.names', emittedEvents.join(','));
70
+ span.setStatus({ code: SpanStatusCode.ERROR, message });
71
+ if (error instanceof Error) {
72
+ span.recordException(error);
73
+ }
74
+ span.end();
75
+ // Restore original emit
76
+ ctx.emit = originalEmit;
77
+ throw error;
78
+ }
79
+ },
80
+ };
81
+ // --- Shutdown flushes pending spans ---
82
+ const shutdown = async () => {
83
+ await provider.shutdown();
84
+ };
85
+ return { middleware, shutdown };
86
+ }
87
+ //# sourceMappingURL=middleware.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"middleware.js","sourceRoot":"","sources":["../../src/middleware.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ export interface OtelOptions {
2
+ /** OTLP endpoint URL (e.g., http://localhost:4318/v1/traces) */
3
+ endpoint: string;
4
+ /** Service name for span attributes */
5
+ serviceName: string;
6
+ /** Additional resource attributes */
7
+ attributes?: Record<string, string>;
8
+ /** Export protocol (default: 'http') */
9
+ protocol?: 'http';
10
+ /** Headers to send with OTLP requests (e.g., for auth) */
11
+ headers?: Record<string, string>;
12
+ }
13
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,WAAW;IAC1B,gEAAgE;IAChE,QAAQ,EAAE,MAAM,CAAC;IACjB,uCAAuC;IACvC,WAAW,EAAE,MAAM,CAAC;IACpB,qCAAqC;IACrC,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,wCAAwC;IACxC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,0DAA0D;IAC1D,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAClC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":""}
package/package.json ADDED
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "intentkit-otel",
3
+ "version": "1.0.0",
4
+ "description": "OpenTelemetry middleware for IntentKit — export telemetry to DataDog, Grafana, New Relic",
5
+ "type": "module",
6
+ "main": "dist/src/index.js",
7
+ "types": "dist/src/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./dist/src/index.js",
11
+ "types": "./dist/src/index.d.ts"
12
+ }
13
+ },
14
+ "files": ["dist", "src", "README.md"],
15
+ "scripts": {
16
+ "build": "tsc",
17
+ "prepublishOnly": "npm run build"
18
+ },
19
+ "peerDependencies": {
20
+ "intentkit": "^1.0.0"
21
+ },
22
+ "dependencies": {
23
+ "@opentelemetry/api": "^1.9.0",
24
+ "@opentelemetry/sdk-trace-base": "^1.30.0",
25
+ "@opentelemetry/exporter-trace-otlp-http": "^0.57.0"
26
+ },
27
+ "devDependencies": {
28
+ "intentkit": "^1.0.0",
29
+ "typescript": "^5.8.2"
30
+ },
31
+ "engines": { "node": ">=22.0.0" },
32
+ "license": "MIT",
33
+ "repository": { "type": "git", "url": "https://github.com/mosoahmed/intentkit" },
34
+ "keywords": ["intentkit", "mcp", "opentelemetry", "otel", "telemetry", "tracing", "ai-agent"]
35
+ }
package/src/index.ts ADDED
@@ -0,0 +1,2 @@
1
+ export { createOtelMiddleware } from './middleware.js';
2
+ export type { OtelOptions } from './types.js';
@@ -0,0 +1,115 @@
1
+ import { trace, SpanStatusCode, type Tracer } from '@opentelemetry/api';
2
+ import { BasicTracerProvider, SimpleSpanProcessor } from '@opentelemetry/sdk-trace-base';
3
+ import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';
4
+ import { Resource } from '@opentelemetry/resources';
5
+ import type { Middleware, IntentContext, RegisteredFunction } from 'intentkit';
6
+ import type { OtelOptions } from './types.js';
7
+
8
+ /**
9
+ * Create an OpenTelemetry tracing middleware for IntentKit.
10
+ *
11
+ * Wraps every function call in an OTLP span with attributes for
12
+ * function name, caller role, capabilities, request ID, duration,
13
+ * and emitted events. Exports spans to any OTLP-compatible backend
14
+ * (DataDog, Grafana Cloud, New Relic, Jaeger, etc.).
15
+ *
16
+ * @returns middleware to register via `registry.use()` and a shutdown
17
+ * function to flush pending spans on process exit.
18
+ */
19
+ export function createOtelMiddleware(options: OtelOptions): {
20
+ middleware: Middleware;
21
+ shutdown: () => Promise<void>;
22
+ } {
23
+ // --- Setup exporter + provider ---
24
+ const exporter = new OTLPTraceExporter({
25
+ url: options.endpoint,
26
+ headers: options.headers,
27
+ });
28
+
29
+ const resourceAttributes: Record<string, string> = {
30
+ 'service.name': options.serviceName,
31
+ ...options.attributes,
32
+ };
33
+
34
+ const provider = new BasicTracerProvider({
35
+ resource: new Resource(resourceAttributes),
36
+ });
37
+
38
+ provider.addSpanProcessor(new SimpleSpanProcessor(exporter));
39
+ provider.register();
40
+
41
+ const tracer: Tracer = trace.getTracer('intentkit', '1.0.0');
42
+
43
+ // --- Build middleware ---
44
+ const middleware: Middleware = {
45
+ name: 'intentkit-otel',
46
+ execute: async (
47
+ fn: RegisteredFunction,
48
+ input: unknown,
49
+ ctx: IntentContext,
50
+ next: () => Promise<unknown>,
51
+ ): Promise<unknown> => {
52
+ const span = tracer.startSpan(fn.name);
53
+
54
+ // Set base attributes
55
+ span.setAttribute('intentkit.function.name', fn.name);
56
+ span.setAttribute('intentkit.caller.role', ctx.caller.role);
57
+ span.setAttribute('intentkit.caller.capabilities', ctx.caller.capabilities.join(','));
58
+ span.setAttribute('intentkit.request.id', ctx.requestId);
59
+
60
+ // Intercept ctx.emit to track emitted events
61
+ const emittedEvents: string[] = [];
62
+ const originalEmit = ctx.emit;
63
+ ctx.emit = (eventName: string, data: Record<string, unknown>) => {
64
+ emittedEvents.push(eventName);
65
+ return originalEmit.call(ctx, eventName, data);
66
+ };
67
+
68
+ const startTime = Date.now();
69
+
70
+ try {
71
+ const result = await next();
72
+
73
+ // Success attributes
74
+ span.setAttribute('intentkit.status', 'success');
75
+ span.setAttribute('intentkit.duration_ms', Date.now() - startTime);
76
+ span.setAttribute('intentkit.events.count', emittedEvents.length);
77
+ span.setAttribute('intentkit.events.names', emittedEvents.join(','));
78
+ span.setStatus({ code: SpanStatusCode.OK });
79
+ span.end();
80
+
81
+ // Restore original emit
82
+ ctx.emit = originalEmit;
83
+
84
+ return result;
85
+ } catch (error) {
86
+ const message = error instanceof Error ? error.message : String(error);
87
+
88
+ // Error attributes
89
+ span.setAttribute('intentkit.status', 'error');
90
+ span.setAttribute('intentkit.duration_ms', Date.now() - startTime);
91
+ span.setAttribute('intentkit.events.count', emittedEvents.length);
92
+ span.setAttribute('intentkit.events.names', emittedEvents.join(','));
93
+ span.setStatus({ code: SpanStatusCode.ERROR, message });
94
+
95
+ if (error instanceof Error) {
96
+ span.recordException(error);
97
+ }
98
+
99
+ span.end();
100
+
101
+ // Restore original emit
102
+ ctx.emit = originalEmit;
103
+
104
+ throw error;
105
+ }
106
+ },
107
+ };
108
+
109
+ // --- Shutdown flushes pending spans ---
110
+ const shutdown = async (): Promise<void> => {
111
+ await provider.shutdown();
112
+ };
113
+
114
+ return { middleware, shutdown };
115
+ }
package/src/types.ts ADDED
@@ -0,0 +1,12 @@
1
+ export interface OtelOptions {
2
+ /** OTLP endpoint URL (e.g., http://localhost:4318/v1/traces) */
3
+ endpoint: string;
4
+ /** Service name for span attributes */
5
+ serviceName: string;
6
+ /** Additional resource attributes */
7
+ attributes?: Record<string, string>;
8
+ /** Export protocol (default: 'http') */
9
+ protocol?: 'http';
10
+ /** Headers to send with OTLP requests (e.g., for auth) */
11
+ headers?: Record<string, string>;
12
+ }