@glassflow-ai/rius 0.6.0 → 0.7.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 CHANGED
@@ -247,7 +247,9 @@ Traces are posted to `<endpoint>/v1/traces`.
247
247
  completions, tool arguments) is attached to spans. It defaults to
248
248
  `true`. Set it to `false`, or supply a `mask` function, if your spans
249
249
  must not carry raw content. Both apply to span attributes and to the
250
- attributes of span events and links. With `captureContent: false` the
250
+ attributes of span events and links, including the `metadata.*` copies the
251
+ Vercel AI SDK transform makes of attributes it does not translate: a
252
+ `metadata.<key>` is content exactly when `<key>` is. With `captureContent: false` the
251
253
  message and stacktrace of a recorded exception are stripped as well,
252
254
  since provider errors routinely echo the request back; the exception
253
255
  event and its `exception.type` are kept, so failures stay visible.
package/dist/index.cjs CHANGED
@@ -373,7 +373,7 @@ function kindAttributes(kind) {
373
373
  if (operation !== void 0) attributes[GEN_AI_OPERATION_NAME] = operation;
374
374
  return attributes;
375
375
  }
376
- var TRACER_NAME, SERVICE_INSTANCE_ID, OPENINFERENCE_SPAN_KIND, INPUT_VALUE, OUTPUT_VALUE, SESSION_ID, USER_ID, WORKSPACE_ROUTE, GEN_AI_OPERATION_NAME, GEN_AI_PROVIDER_NAME, GEN_AI_REQUEST_MODEL, GEN_AI_REQUEST_REASONING_LEVEL, GEN_AI_RESPONSE_MODEL, GEN_AI_USAGE_INPUT_TOKENS, GEN_AI_USAGE_OUTPUT_TOKENS, GEN_AI_USAGE_CACHE_READ_INPUT_TOKENS, GEN_AI_USAGE_CACHE_WRITE_INPUT_TOKENS, GEN_AI_USAGE_REASONING_OUTPUT_TOKENS, GEN_AI_INPUT_MESSAGES, GEN_AI_OUTPUT_MESSAGES, GEN_AI_RESPONSE_FINISH_REASONS, GEN_AI_TOOL_NAME, GEN_AI_TOOL_DEFINITIONS, GEN_AI_REQUEST_PREFIX, MCP_RESULT_TYPE, GEN_AI_FIRST_TOKEN_EVENT, SpanKind, OPERATION_BY_KIND, CONTENT_ATTRIBUTES, LLM_INVOCATION_PARAMETERS, INVOCATION_PARAMETERS_CONTENT_MEMBERS, CONTENT_ATTRIBUTE_PREFIXES, CONTENT_ATTRIBUTE_SUFFIXES, GLASSFLOW_SPAN_PENDING, PENDING_IDENTITY_ATTRIBUTES, PENDING_IDENTITY_PREFIXES;
376
+ var TRACER_NAME, SERVICE_INSTANCE_ID, OPENINFERENCE_SPAN_KIND, INPUT_VALUE, OUTPUT_VALUE, SESSION_ID, USER_ID, WORKSPACE_ROUTE, GEN_AI_OPERATION_NAME, GEN_AI_PROVIDER_NAME, GEN_AI_REQUEST_MODEL, GEN_AI_REQUEST_REASONING_LEVEL, GEN_AI_RESPONSE_MODEL, GEN_AI_USAGE_INPUT_TOKENS, GEN_AI_USAGE_OUTPUT_TOKENS, GEN_AI_USAGE_CACHE_READ_INPUT_TOKENS, GEN_AI_USAGE_CACHE_WRITE_INPUT_TOKENS, GEN_AI_USAGE_REASONING_OUTPUT_TOKENS, GEN_AI_INPUT_MESSAGES, GEN_AI_OUTPUT_MESSAGES, GEN_AI_RESPONSE_FINISH_REASONS, GEN_AI_TOOL_NAME, GEN_AI_TOOL_DEFINITIONS, GEN_AI_REQUEST_PREFIX, MCP_METHOD_NAME, MCP_METHOD_TOOLS_CALL, MCP_PROTOCOL_VERSION, ERROR_TYPE, ERROR_TYPE_TOOL_ERROR, MCP_RESULT_TYPE, GEN_AI_FIRST_TOKEN_EVENT, SpanKind, OPERATION_BY_KIND, CONTENT_ATTRIBUTES, LLM_INVOCATION_PARAMETERS, INVOCATION_PARAMETERS_CONTENT_MEMBERS, CONTENT_ATTRIBUTE_PREFIXES, CONTENT_ATTRIBUTE_SUFFIXES, GLASSFLOW_SPAN_PENDING, PENDING_IDENTITY_ATTRIBUTES, PENDING_IDENTITY_PREFIXES;
377
377
  var init_semconv = __esm({
378
378
  "src/semconv.ts"() {
379
379
  "use strict";
@@ -402,6 +402,11 @@ var init_semconv = __esm({
402
402
  GEN_AI_TOOL_NAME = "gen_ai.tool.name";
403
403
  GEN_AI_TOOL_DEFINITIONS = "gen_ai.tool.definitions";
404
404
  GEN_AI_REQUEST_PREFIX = "gen_ai.request.";
405
+ MCP_METHOD_NAME = "mcp.method.name";
406
+ MCP_METHOD_TOOLS_CALL = "tools/call";
407
+ MCP_PROTOCOL_VERSION = "mcp.protocol.version";
408
+ ERROR_TYPE = "error.type";
409
+ ERROR_TYPE_TOOL_ERROR = "tool_error";
405
410
  MCP_RESULT_TYPE = "mcp.result_type";
406
411
  GEN_AI_FIRST_TOKEN_EVENT = "gen_ai.first_token";
407
412
  SpanKind = /* @__PURE__ */ ((SpanKind2) => {
@@ -503,6 +508,11 @@ var init_semconv = __esm({
503
508
  GEN_AI_OPERATION_NAME,
504
509
  GEN_AI_PROVIDER_NAME,
505
510
  GEN_AI_TOOL_NAME,
511
+ // Protocol identity, not content: a still-running MCP call must be
512
+ // distinguishable from a local tool in the live view — the one place
513
+ // setting the marker at creation pays off.
514
+ MCP_METHOD_NAME,
515
+ MCP_PROTOCOL_VERSION,
506
516
  // Identity, not content: a pending span must be groupable into its
507
517
  // session while still running, that is the live view's whole point.
508
518
  SESSION_ID,
@@ -531,6 +541,9 @@ function renderable(raw, value) {
531
541
  if (raw instanceof ArrayBuffer) return `<ArrayBuffer ${raw.byteLength} bytes>`;
532
542
  return value;
533
543
  }
544
+ function asRecord(value) {
545
+ return typeof value === "object" && value !== null ? value : void 0;
546
+ }
534
547
  function toAttributeValue(value) {
535
548
  if (typeof value === "string") return truncate(value);
536
549
  if (typeof value === "number" || typeof value === "boolean") return value;
@@ -603,7 +616,10 @@ function creationAttributes(options) {
603
616
  return attributes;
604
617
  }
605
618
  function startSpan(name, options = {}) {
606
- const span = getTracer().startSpan(name, { attributes: creationAttributes(options) });
619
+ const span = getTracer().startSpan(name, {
620
+ kind: options.otelKind,
621
+ attributes: creationAttributes(options)
622
+ });
607
623
  return configure(new Observation(span), options);
608
624
  }
609
625
  function startAsCurrentSpan(name, optionsOrFn, maybeFn) {
@@ -613,11 +629,12 @@ function startAsCurrentSpan(name, optionsOrFn, maybeFn) {
613
629
  creationAttributes(options),
614
630
  options.userId,
615
631
  (span) => configure(new Observation(span), options),
616
- fn
632
+ fn,
633
+ options.otelKind
617
634
  );
618
635
  }
619
- function runActive(name, attributes, userId, makeHandle, fn) {
620
- const run = () => getTracer().startActiveSpan(name, { attributes }, async (span) => {
636
+ function runActive(name, attributes, userId, makeHandle, fn, otelKind) {
637
+ const run = () => getTracer().startActiveSpan(name, { kind: otelKind, attributes }, async (span) => {
621
638
  const handle = makeHandle(span);
622
639
  try {
623
640
  return await fn(handle);
@@ -711,27 +728,25 @@ __export(instrumentationMcp_exports, {
711
728
  instrumentMcpClient: () => instrumentMcpClient
712
729
  });
713
730
  function serializeResult(result) {
714
- if (typeof result !== "object" || result === null) return toAttributeValue(result);
715
- const record = result;
731
+ const record = asRecord(result);
732
+ if (record === void 0) return toAttributeValue(result);
716
733
  const structured = record.structured_content ?? record.structuredContent;
717
734
  if (structured !== void 0 && structured !== null) return toAttributeValue(structured);
718
735
  const content = record.content;
719
736
  if (Array.isArray(content)) {
720
- const texts = content.map(
721
- (block) => typeof block === "object" && block !== null ? block.text : void 0
722
- ).filter((text) => typeof text === "string");
737
+ const texts = content.map((block) => asRecord(block)?.text).filter((text) => typeof text === "string");
723
738
  if (texts.length === 1) return truncate(texts[0]);
724
739
  if (texts.length > 1) return toAttributeValue(texts);
725
740
  }
726
741
  return toAttributeValue(result);
727
742
  }
728
743
  function resultIsError(result) {
729
- if (typeof result !== "object" || result === null) return false;
730
- const record = result;
731
- return Boolean(record.is_error ?? record.isError);
744
+ const record = asRecord(result);
745
+ return Boolean(record?.is_error ?? record?.isError);
732
746
  }
733
747
  function recordResult(observation, result) {
734
- const resultType = typeof result === "object" && result !== null ? result.result_type ?? result.resultType : void 0;
748
+ const record = asRecord(result);
749
+ const resultType = record?.result_type ?? record?.resultType;
735
750
  if (resultType === INPUT_REQUIRED) {
736
751
  observation.setAttribute(MCP_RESULT_TYPE, INPUT_REQUIRED);
737
752
  return;
@@ -742,8 +757,24 @@ function recordResult(observation, result) {
742
757
  code: import_api3.SpanStatusCode.ERROR,
743
758
  message: "tool returned an error result"
744
759
  });
760
+ observation.setAttribute(ERROR_TYPE, ERROR_TYPE_TOOL_ERROR);
745
761
  }
746
762
  }
763
+ function errorType(error) {
764
+ return error instanceof Error ? error.name : typeof error;
765
+ }
766
+ function negotiatedProtocolVersion(client) {
767
+ const version = asRecord(asRecord(client)?.transport)?.protocolVersion;
768
+ return typeof version === "string" ? version : void 0;
769
+ }
770
+ function callAttributes(toolName, protocolVersion) {
771
+ const attributes = {
772
+ [GEN_AI_TOOL_NAME]: toolName,
773
+ [MCP_METHOD_NAME]: MCP_METHOD_TOOLS_CALL
774
+ };
775
+ if (protocolVersion !== void 0) attributes[MCP_PROTOCOL_VERSION] = protocolVersion;
776
+ return attributes;
777
+ }
747
778
  function instrumentMcpClient(ClientClass) {
748
779
  const current = ClientClass.prototype.callTool;
749
780
  const alreadyWrapped = current.riusOriginal;
@@ -756,15 +787,23 @@ function instrumentMcpClient(ClientClass) {
756
787
  const instrumented = function instrumentedCallTool(params, ...rest) {
757
788
  return startAsCurrentSpan(
758
789
  `execute_tool ${params.name}`,
759
- // Tool name at CREATION: pending snapshots are built at start, so an
760
- // attribute set inside the callback never reaches them.
761
790
  {
762
791
  kind: "TOOL" /* TOOL */,
792
+ // The OTel SpanKind FIELD (orthogonal to our taxonomy attribute above):
793
+ // a remote tool call is CLIENT under both the MCP and the GenAI
794
+ // execute-tool conventions.
795
+ otelKind: import_api3.SpanKind.CLIENT,
763
796
  input: params.arguments,
764
- attributes: { [GEN_AI_TOOL_NAME]: params.name }
797
+ attributes: callAttributes(params.name, negotiatedProtocolVersion(this))
765
798
  },
766
799
  async (observation) => {
767
- const result = await original.call(this, params, ...rest);
800
+ let result;
801
+ try {
802
+ result = await original.call(this, params, ...rest);
803
+ } catch (error) {
804
+ observation.setAttribute(ERROR_TYPE, errorType(error));
805
+ throw error;
806
+ }
768
807
  recordResult(observation, result);
769
808
  return result;
770
809
  }
@@ -1034,7 +1073,8 @@ var init_instrumentation = __esm({
1034
1073
  function isContentKey(key) {
1035
1074
  if (CONTENT_ATTRIBUTES.has(key)) return true;
1036
1075
  if (CONTENT_ATTRIBUTE_PREFIXES.some((p) => key.startsWith(p))) return true;
1037
- return CONTENT_ATTRIBUTE_SUFFIXES.some((s) => key.endsWith(s));
1076
+ if (CONTENT_ATTRIBUTE_SUFFIXES.some((s) => key.endsWith(s))) return true;
1077
+ return key.startsWith(METADATA_PREFIX) && isContentKey(key.slice(METADATA_PREFIX.length));
1038
1078
  }
1039
1079
  function redactInvocationParameters(value) {
1040
1080
  if (typeof value !== "string") return void 0;
@@ -1054,7 +1094,7 @@ function redactInvocationParameters(value) {
1054
1094
  for (const member of INVOCATION_PARAMETERS_CONTENT_MEMBERS) delete bag[member];
1055
1095
  return JSON.stringify(bag);
1056
1096
  }
1057
- var import_api4, EXCEPTION_EVENT_NAME, EXCEPTION_CONTENT_KEYS, STATUS_DESCRIPTION_KEY, MaskingSpanExporter;
1097
+ var import_api4, METADATA_PREFIX, EXCEPTION_EVENT_NAME, EXCEPTION_CONTENT_KEYS, STATUS_DESCRIPTION_KEY, MaskingSpanExporter;
1058
1098
  var init_masking = __esm({
1059
1099
  "src/masking.ts"() {
1060
1100
  "use strict";
@@ -1062,6 +1102,7 @@ var init_masking = __esm({
1062
1102
  import_api4 = require("@opentelemetry/api");
1063
1103
  init_semconv();
1064
1104
  init_serde();
1105
+ METADATA_PREFIX = "metadata.";
1065
1106
  EXCEPTION_EVENT_NAME = "exception";
1066
1107
  EXCEPTION_CONTENT_KEYS = ["exception.message", "exception.stacktrace"];
1067
1108
  STATUS_DESCRIPTION_KEY = "status.description";
@@ -1833,7 +1874,7 @@ init_session();
1833
1874
  init_user();
1834
1875
  init_spans();
1835
1876
  init_workspace();
1836
- var VERSION = "0.6.0";
1877
+ var VERSION = "0.7.0";
1837
1878
  // Annotate the CommonJS export names for ESM import in node:
1838
1879
  0 && (module.exports = {
1839
1880
  Generation,
package/dist/index.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { Tracer, Span } from '@opentelemetry/api';
1
+ import { Tracer, Span, SpanKind as SpanKind$1 } from '@opentelemetry/api';
2
2
  import { SpanExporter } from '@opentelemetry/sdk-trace-base';
3
3
 
4
4
  /** Redacts content attribute values at export. Receives the key when it accepts one. */
@@ -171,6 +171,13 @@ declare enum SpanKind {
171
171
  /** Options for {@link startSpan} and {@link startAsCurrentSpan}. */
172
172
  interface SpanOptions {
173
173
  kind?: SpanKind;
174
+ /**
175
+ * The OpenTelemetry `SpanKind` FIELD (INTERNAL, CLIENT, …), orthogonal to
176
+ * `kind` above, which is our taxonomy attribute. Conventions set it per
177
+ * operation — a remote tool call is CLIENT — so it is opt-in here and left
178
+ * at the tracer's default (INTERNAL) otherwise.
179
+ */
180
+ otelKind?: SpanKind$1;
174
181
  input?: unknown;
175
182
  /**
176
183
  * End-user identity (`user.id`). Sugar for `withUser`: set on this span at
@@ -441,6 +448,6 @@ declare function withSession<T>(sessionId: string, fn: (sessionId: string) => T)
441
448
  */
442
449
  declare function withUser<T>(userId: string, fn: (userId: string) => T): T;
443
450
 
444
- declare const VERSION = "0.6.0";
451
+ declare const VERSION = "0.7.0";
445
452
 
446
453
  export { Generation, type GenerationBody, type GenerationOptions, type InitOptions, type Mask, Observation, type ObserveOptions, RiusClient, type RiusOptions, type SpanBody, SpanKind, type SpanOptions, VERSION, type WorkspaceExporterFactory, getTracer, init, observe, registerWorkspace, startAsCurrentGeneration, startAsCurrentSpan, startGeneration, startSpan, withSession, withUser, withWorkspace };
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { Tracer, Span } from '@opentelemetry/api';
1
+ import { Tracer, Span, SpanKind as SpanKind$1 } from '@opentelemetry/api';
2
2
  import { SpanExporter } from '@opentelemetry/sdk-trace-base';
3
3
 
4
4
  /** Redacts content attribute values at export. Receives the key when it accepts one. */
@@ -171,6 +171,13 @@ declare enum SpanKind {
171
171
  /** Options for {@link startSpan} and {@link startAsCurrentSpan}. */
172
172
  interface SpanOptions {
173
173
  kind?: SpanKind;
174
+ /**
175
+ * The OpenTelemetry `SpanKind` FIELD (INTERNAL, CLIENT, …), orthogonal to
176
+ * `kind` above, which is our taxonomy attribute. Conventions set it per
177
+ * operation — a remote tool call is CLIENT — so it is opt-in here and left
178
+ * at the tracer's default (INTERNAL) otherwise.
179
+ */
180
+ otelKind?: SpanKind$1;
174
181
  input?: unknown;
175
182
  /**
176
183
  * End-user identity (`user.id`). Sugar for `withUser`: set on this span at
@@ -441,6 +448,6 @@ declare function withSession<T>(sessionId: string, fn: (sessionId: string) => T)
441
448
  */
442
449
  declare function withUser<T>(userId: string, fn: (userId: string) => T): T;
443
450
 
444
- declare const VERSION = "0.6.0";
451
+ declare const VERSION = "0.7.0";
445
452
 
446
453
  export { Generation, type GenerationBody, type GenerationOptions, type InitOptions, type Mask, Observation, type ObserveOptions, RiusClient, type RiusOptions, type SpanBody, SpanKind, type SpanOptions, VERSION, type WorkspaceExporterFactory, getTracer, init, observe, registerWorkspace, startAsCurrentGeneration, startAsCurrentSpan, startGeneration, startSpan, withSession, withUser, withWorkspace };
package/dist/index.js CHANGED
@@ -360,7 +360,7 @@ function kindAttributes(kind) {
360
360
  if (operation !== void 0) attributes[GEN_AI_OPERATION_NAME] = operation;
361
361
  return attributes;
362
362
  }
363
- var TRACER_NAME, SERVICE_INSTANCE_ID, OPENINFERENCE_SPAN_KIND, INPUT_VALUE, OUTPUT_VALUE, SESSION_ID, USER_ID, WORKSPACE_ROUTE, GEN_AI_OPERATION_NAME, GEN_AI_PROVIDER_NAME, GEN_AI_REQUEST_MODEL, GEN_AI_REQUEST_REASONING_LEVEL, GEN_AI_RESPONSE_MODEL, GEN_AI_USAGE_INPUT_TOKENS, GEN_AI_USAGE_OUTPUT_TOKENS, GEN_AI_USAGE_CACHE_READ_INPUT_TOKENS, GEN_AI_USAGE_CACHE_WRITE_INPUT_TOKENS, GEN_AI_USAGE_REASONING_OUTPUT_TOKENS, GEN_AI_INPUT_MESSAGES, GEN_AI_OUTPUT_MESSAGES, GEN_AI_RESPONSE_FINISH_REASONS, GEN_AI_TOOL_NAME, GEN_AI_TOOL_DEFINITIONS, GEN_AI_REQUEST_PREFIX, MCP_RESULT_TYPE, GEN_AI_FIRST_TOKEN_EVENT, SpanKind, OPERATION_BY_KIND, CONTENT_ATTRIBUTES, LLM_INVOCATION_PARAMETERS, INVOCATION_PARAMETERS_CONTENT_MEMBERS, CONTENT_ATTRIBUTE_PREFIXES, CONTENT_ATTRIBUTE_SUFFIXES, GLASSFLOW_SPAN_PENDING, PENDING_IDENTITY_ATTRIBUTES, PENDING_IDENTITY_PREFIXES;
363
+ var TRACER_NAME, SERVICE_INSTANCE_ID, OPENINFERENCE_SPAN_KIND, INPUT_VALUE, OUTPUT_VALUE, SESSION_ID, USER_ID, WORKSPACE_ROUTE, GEN_AI_OPERATION_NAME, GEN_AI_PROVIDER_NAME, GEN_AI_REQUEST_MODEL, GEN_AI_REQUEST_REASONING_LEVEL, GEN_AI_RESPONSE_MODEL, GEN_AI_USAGE_INPUT_TOKENS, GEN_AI_USAGE_OUTPUT_TOKENS, GEN_AI_USAGE_CACHE_READ_INPUT_TOKENS, GEN_AI_USAGE_CACHE_WRITE_INPUT_TOKENS, GEN_AI_USAGE_REASONING_OUTPUT_TOKENS, GEN_AI_INPUT_MESSAGES, GEN_AI_OUTPUT_MESSAGES, GEN_AI_RESPONSE_FINISH_REASONS, GEN_AI_TOOL_NAME, GEN_AI_TOOL_DEFINITIONS, GEN_AI_REQUEST_PREFIX, MCP_METHOD_NAME, MCP_METHOD_TOOLS_CALL, MCP_PROTOCOL_VERSION, ERROR_TYPE, ERROR_TYPE_TOOL_ERROR, MCP_RESULT_TYPE, GEN_AI_FIRST_TOKEN_EVENT, SpanKind, OPERATION_BY_KIND, CONTENT_ATTRIBUTES, LLM_INVOCATION_PARAMETERS, INVOCATION_PARAMETERS_CONTENT_MEMBERS, CONTENT_ATTRIBUTE_PREFIXES, CONTENT_ATTRIBUTE_SUFFIXES, GLASSFLOW_SPAN_PENDING, PENDING_IDENTITY_ATTRIBUTES, PENDING_IDENTITY_PREFIXES;
364
364
  var init_semconv = __esm({
365
365
  "src/semconv.ts"() {
366
366
  "use strict";
@@ -389,6 +389,11 @@ var init_semconv = __esm({
389
389
  GEN_AI_TOOL_NAME = "gen_ai.tool.name";
390
390
  GEN_AI_TOOL_DEFINITIONS = "gen_ai.tool.definitions";
391
391
  GEN_AI_REQUEST_PREFIX = "gen_ai.request.";
392
+ MCP_METHOD_NAME = "mcp.method.name";
393
+ MCP_METHOD_TOOLS_CALL = "tools/call";
394
+ MCP_PROTOCOL_VERSION = "mcp.protocol.version";
395
+ ERROR_TYPE = "error.type";
396
+ ERROR_TYPE_TOOL_ERROR = "tool_error";
392
397
  MCP_RESULT_TYPE = "mcp.result_type";
393
398
  GEN_AI_FIRST_TOKEN_EVENT = "gen_ai.first_token";
394
399
  SpanKind = /* @__PURE__ */ ((SpanKind2) => {
@@ -490,6 +495,11 @@ var init_semconv = __esm({
490
495
  GEN_AI_OPERATION_NAME,
491
496
  GEN_AI_PROVIDER_NAME,
492
497
  GEN_AI_TOOL_NAME,
498
+ // Protocol identity, not content: a still-running MCP call must be
499
+ // distinguishable from a local tool in the live view — the one place
500
+ // setting the marker at creation pays off.
501
+ MCP_METHOD_NAME,
502
+ MCP_PROTOCOL_VERSION,
493
503
  // Identity, not content: a pending span must be groupable into its
494
504
  // session while still running, that is the live view's whole point.
495
505
  SESSION_ID,
@@ -518,6 +528,9 @@ function renderable(raw, value) {
518
528
  if (raw instanceof ArrayBuffer) return `<ArrayBuffer ${raw.byteLength} bytes>`;
519
529
  return value;
520
530
  }
531
+ function asRecord(value) {
532
+ return typeof value === "object" && value !== null ? value : void 0;
533
+ }
521
534
  function toAttributeValue(value) {
522
535
  if (typeof value === "string") return truncate(value);
523
536
  if (typeof value === "number" || typeof value === "boolean") return value;
@@ -591,7 +604,10 @@ function creationAttributes(options) {
591
604
  return attributes;
592
605
  }
593
606
  function startSpan(name, options = {}) {
594
- const span = getTracer().startSpan(name, { attributes: creationAttributes(options) });
607
+ const span = getTracer().startSpan(name, {
608
+ kind: options.otelKind,
609
+ attributes: creationAttributes(options)
610
+ });
595
611
  return configure(new Observation(span), options);
596
612
  }
597
613
  function startAsCurrentSpan(name, optionsOrFn, maybeFn) {
@@ -601,11 +617,12 @@ function startAsCurrentSpan(name, optionsOrFn, maybeFn) {
601
617
  creationAttributes(options),
602
618
  options.userId,
603
619
  (span) => configure(new Observation(span), options),
604
- fn
620
+ fn,
621
+ options.otelKind
605
622
  );
606
623
  }
607
- function runActive(name, attributes, userId, makeHandle, fn) {
608
- const run = () => getTracer().startActiveSpan(name, { attributes }, async (span) => {
624
+ function runActive(name, attributes, userId, makeHandle, fn, otelKind) {
625
+ const run = () => getTracer().startActiveSpan(name, { kind: otelKind, attributes }, async (span) => {
609
626
  const handle = makeHandle(span);
610
627
  try {
611
628
  return await fn(handle);
@@ -697,29 +714,27 @@ var instrumentationMcp_exports = {};
697
714
  __export(instrumentationMcp_exports, {
698
715
  instrumentMcpClient: () => instrumentMcpClient
699
716
  });
700
- import { SpanStatusCode as SpanStatusCode2 } from "@opentelemetry/api";
717
+ import { SpanKind as OtelSpanKind, SpanStatusCode as SpanStatusCode2 } from "@opentelemetry/api";
701
718
  function serializeResult(result) {
702
- if (typeof result !== "object" || result === null) return toAttributeValue(result);
703
- const record = result;
719
+ const record = asRecord(result);
720
+ if (record === void 0) return toAttributeValue(result);
704
721
  const structured = record.structured_content ?? record.structuredContent;
705
722
  if (structured !== void 0 && structured !== null) return toAttributeValue(structured);
706
723
  const content = record.content;
707
724
  if (Array.isArray(content)) {
708
- const texts = content.map(
709
- (block) => typeof block === "object" && block !== null ? block.text : void 0
710
- ).filter((text) => typeof text === "string");
725
+ const texts = content.map((block) => asRecord(block)?.text).filter((text) => typeof text === "string");
711
726
  if (texts.length === 1) return truncate(texts[0]);
712
727
  if (texts.length > 1) return toAttributeValue(texts);
713
728
  }
714
729
  return toAttributeValue(result);
715
730
  }
716
731
  function resultIsError(result) {
717
- if (typeof result !== "object" || result === null) return false;
718
- const record = result;
719
- return Boolean(record.is_error ?? record.isError);
732
+ const record = asRecord(result);
733
+ return Boolean(record?.is_error ?? record?.isError);
720
734
  }
721
735
  function recordResult(observation, result) {
722
- const resultType = typeof result === "object" && result !== null ? result.result_type ?? result.resultType : void 0;
736
+ const record = asRecord(result);
737
+ const resultType = record?.result_type ?? record?.resultType;
723
738
  if (resultType === INPUT_REQUIRED) {
724
739
  observation.setAttribute(MCP_RESULT_TYPE, INPUT_REQUIRED);
725
740
  return;
@@ -730,8 +745,24 @@ function recordResult(observation, result) {
730
745
  code: SpanStatusCode2.ERROR,
731
746
  message: "tool returned an error result"
732
747
  });
748
+ observation.setAttribute(ERROR_TYPE, ERROR_TYPE_TOOL_ERROR);
733
749
  }
734
750
  }
751
+ function errorType(error) {
752
+ return error instanceof Error ? error.name : typeof error;
753
+ }
754
+ function negotiatedProtocolVersion(client) {
755
+ const version = asRecord(asRecord(client)?.transport)?.protocolVersion;
756
+ return typeof version === "string" ? version : void 0;
757
+ }
758
+ function callAttributes(toolName, protocolVersion) {
759
+ const attributes = {
760
+ [GEN_AI_TOOL_NAME]: toolName,
761
+ [MCP_METHOD_NAME]: MCP_METHOD_TOOLS_CALL
762
+ };
763
+ if (protocolVersion !== void 0) attributes[MCP_PROTOCOL_VERSION] = protocolVersion;
764
+ return attributes;
765
+ }
735
766
  function instrumentMcpClient(ClientClass) {
736
767
  const current = ClientClass.prototype.callTool;
737
768
  const alreadyWrapped = current.riusOriginal;
@@ -744,15 +775,23 @@ function instrumentMcpClient(ClientClass) {
744
775
  const instrumented = function instrumentedCallTool(params, ...rest) {
745
776
  return startAsCurrentSpan(
746
777
  `execute_tool ${params.name}`,
747
- // Tool name at CREATION: pending snapshots are built at start, so an
748
- // attribute set inside the callback never reaches them.
749
778
  {
750
779
  kind: "TOOL" /* TOOL */,
780
+ // The OTel SpanKind FIELD (orthogonal to our taxonomy attribute above):
781
+ // a remote tool call is CLIENT under both the MCP and the GenAI
782
+ // execute-tool conventions.
783
+ otelKind: OtelSpanKind.CLIENT,
751
784
  input: params.arguments,
752
- attributes: { [GEN_AI_TOOL_NAME]: params.name }
785
+ attributes: callAttributes(params.name, negotiatedProtocolVersion(this))
753
786
  },
754
787
  async (observation) => {
755
- const result = await original.call(this, params, ...rest);
788
+ let result;
789
+ try {
790
+ result = await original.call(this, params, ...rest);
791
+ } catch (error) {
792
+ observation.setAttribute(ERROR_TYPE, errorType(error));
793
+ throw error;
794
+ }
756
795
  recordResult(observation, result);
757
796
  return result;
758
797
  }
@@ -1022,7 +1061,8 @@ import { SpanStatusCode as SpanStatusCode3 } from "@opentelemetry/api";
1022
1061
  function isContentKey(key) {
1023
1062
  if (CONTENT_ATTRIBUTES.has(key)) return true;
1024
1063
  if (CONTENT_ATTRIBUTE_PREFIXES.some((p) => key.startsWith(p))) return true;
1025
- return CONTENT_ATTRIBUTE_SUFFIXES.some((s) => key.endsWith(s));
1064
+ if (CONTENT_ATTRIBUTE_SUFFIXES.some((s) => key.endsWith(s))) return true;
1065
+ return key.startsWith(METADATA_PREFIX) && isContentKey(key.slice(METADATA_PREFIX.length));
1026
1066
  }
1027
1067
  function redactInvocationParameters(value) {
1028
1068
  if (typeof value !== "string") return void 0;
@@ -1042,13 +1082,14 @@ function redactInvocationParameters(value) {
1042
1082
  for (const member of INVOCATION_PARAMETERS_CONTENT_MEMBERS) delete bag[member];
1043
1083
  return JSON.stringify(bag);
1044
1084
  }
1045
- var EXCEPTION_EVENT_NAME, EXCEPTION_CONTENT_KEYS, STATUS_DESCRIPTION_KEY, MaskingSpanExporter;
1085
+ var METADATA_PREFIX, EXCEPTION_EVENT_NAME, EXCEPTION_CONTENT_KEYS, STATUS_DESCRIPTION_KEY, MaskingSpanExporter;
1046
1086
  var init_masking = __esm({
1047
1087
  "src/masking.ts"() {
1048
1088
  "use strict";
1049
1089
  init_esm_shims();
1050
1090
  init_semconv();
1051
1091
  init_serde();
1092
+ METADATA_PREFIX = "metadata.";
1052
1093
  EXCEPTION_EVENT_NAME = "exception";
1053
1094
  EXCEPTION_CONTENT_KEYS = ["exception.message", "exception.stacktrace"];
1054
1095
  STATUS_DESCRIPTION_KEY = "status.description";
@@ -1804,7 +1845,7 @@ init_session();
1804
1845
  init_user();
1805
1846
  init_spans();
1806
1847
  init_workspace();
1807
- var VERSION = "0.6.0";
1848
+ var VERSION = "0.7.0";
1808
1849
  export {
1809
1850
  Generation,
1810
1851
  Observation,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@glassflow-ai/rius",
3
- "version": "0.6.0",
3
+ "version": "0.7.0",
4
4
  "description": "OpenTelemetry-native tracing for AI agents and LLM applications",
5
5
  "keywords": [
6
6
  "opentelemetry",