@reactionary/core 0.10.34 → 0.10.35

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/index.js CHANGED
@@ -5,3 +5,4 @@ export * from "./factories/index.js";
5
5
  export * from "./capabilities/index.js";
6
6
  export * from "./schemas/index.js";
7
7
  export * from "./initialization.js";
8
+ export * from "./metrics/protocol.js";
@@ -0,0 +1,84 @@
1
+ import {
2
+ metrics,
3
+ SpanStatusCode,
4
+ trace
5
+ } from "@opentelemetry/api";
6
+ const METER_NAME = "@reactionary";
7
+ const METER_VERSION = "0.0.1";
8
+ let globalProtocolMetrics = null;
9
+ function getReactionaryProtocolMeter() {
10
+ if (!globalProtocolMetrics) {
11
+ const meter = metrics.getMeter(METER_NAME, METER_VERSION);
12
+ globalProtocolMetrics = {
13
+ requests: meter.createCounter("reactionary_protocol_requests", {
14
+ description: "Counts the number of protocol invocations (UCP, ACP, MCP) served by Reactionary protocol servers"
15
+ }),
16
+ requestDuration: meter.createHistogram(
17
+ "reactionary_protocol_request_duration",
18
+ {
19
+ description: "Records the duration of protocol invocations (UCP, ACP, MCP) served by Reactionary protocol servers",
20
+ unit: "ms"
21
+ }
22
+ ),
23
+ requestsInProgress: meter.createUpDownCounter(
24
+ "reactionary_protocol_requests_in_progress",
25
+ {
26
+ description: "Tracks the number of in-progress protocol invocations (UCP, ACP, MCP)"
27
+ }
28
+ )
29
+ };
30
+ }
31
+ return globalProtocolMetrics;
32
+ }
33
+ async function traceProtocolInvocation(invocation, fn, getResultAttributes) {
34
+ const meter = getReactionaryProtocolMeter();
35
+ const tracer = trace.getTracer(METER_NAME, METER_VERSION);
36
+ const baseAttributes = {
37
+ "labels.protocol": invocation.protocol,
38
+ "labels.operation": invocation.operation,
39
+ ...invocation.attributes
40
+ };
41
+ const startTime = performance.now();
42
+ meter.requestsInProgress.add(1, baseAttributes);
43
+ return tracer.startActiveSpan(
44
+ `${invocation.protocol.toUpperCase()} ${invocation.operation}`,
45
+ { attributes: baseAttributes },
46
+ async (span) => {
47
+ let resultAttributes = { "labels.status": "success" };
48
+ try {
49
+ const result = await fn(span);
50
+ if (getResultAttributes) {
51
+ resultAttributes = { ...resultAttributes, ...getResultAttributes(result) };
52
+ }
53
+ if (resultAttributes["labels.status"] === "error") {
54
+ span.setStatus({ code: SpanStatusCode.ERROR });
55
+ }
56
+ span.setAttributes(resultAttributes);
57
+ return result;
58
+ } catch (err) {
59
+ const message = err instanceof Error ? err.message : String(err);
60
+ resultAttributes = { "labels.status": "exception" };
61
+ span.recordException(err instanceof Error ? err : { message });
62
+ span.setStatus({ code: SpanStatusCode.ERROR, message });
63
+ throw err;
64
+ } finally {
65
+ const finalAttributes = { ...baseAttributes, ...resultAttributes };
66
+ meter.requestsInProgress.add(-1, baseAttributes);
67
+ meter.requests.add(1, finalAttributes);
68
+ meter.requestDuration.record(performance.now() - startTime, finalAttributes);
69
+ span.end();
70
+ }
71
+ }
72
+ );
73
+ }
74
+ function getHttpProtocolResultAttributes(response) {
75
+ return {
76
+ "labels.status": response.status >= 500 ? "error" : "success",
77
+ "http.response.status_code": response.status
78
+ };
79
+ }
80
+ export {
81
+ getHttpProtocolResultAttributes,
82
+ getReactionaryProtocolMeter,
83
+ traceProtocolInvocation
84
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reactionary/core",
3
- "version": "0.10.34",
3
+ "version": "0.10.35",
4
4
  "type": "module",
5
5
  "main": "./index.js",
6
6
  "types": "./src/index.d.ts",
package/src/index.d.ts CHANGED
@@ -5,4 +5,5 @@ export * from './factories/index.js';
5
5
  export * from './capabilities/index.js';
6
6
  export * from './schemas/index.js';
7
7
  export * from './initialization.js';
8
+ export * from './metrics/protocol.js';
8
9
  export type { InferType } from './zod-utils.js';
@@ -0,0 +1,20 @@
1
+ import { type Attributes, type Counter, type Histogram, type Span, type UpDownCounter } from '@opentelemetry/api';
2
+ export interface ReactionaryProtocolMetrics {
3
+ requests: Counter;
4
+ requestDuration: Histogram;
5
+ requestsInProgress: UpDownCounter;
6
+ }
7
+ export declare function getReactionaryProtocolMeter(): ReactionaryProtocolMetrics;
8
+ export interface ReactionaryProtocolInvocation {
9
+ protocol: 'ucp' | 'acp' | 'mcp';
10
+ operation: string;
11
+ attributes?: Attributes;
12
+ }
13
+ /**
14
+ * Wraps a single protocol invocation (an inbound UCP/ACP HTTP request or an
15
+ * MCP tool call) in an OpenTelemetry span and records request count,
16
+ * duration, and in-progress metrics. `getResultAttributes` may add result
17
+ * attributes such as the HTTP status code and override `labels.status`.
18
+ */
19
+ export declare function traceProtocolInvocation<T>(invocation: ReactionaryProtocolInvocation, fn: (span: Span) => Promise<T>, getResultAttributes?: (result: T) => Attributes): Promise<T>;
20
+ export declare function getHttpProtocolResultAttributes(response: Response): Attributes;