@mastra/client-js 1.20.1-alpha.0 → 1.21.0-alpha.2

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/dist/index.cjs CHANGED
@@ -15,6 +15,210 @@ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
15
15
  var canonicalize__default = /*#__PURE__*/_interopDefault(canonicalize);
16
16
 
17
17
  // src/resources/agent.ts
18
+
19
+ // src/observability/collector.ts
20
+ var TRACEPARENT_RE = /^([0-9a-f]{2})-([0-9a-f]{32})-([0-9a-f]{16})-([0-9a-f]{2})$/;
21
+ var ZERO_TRACE_ID = "00000000000000000000000000000000";
22
+ var ZERO_SPAN_ID = "0000000000000000";
23
+ var LEVEL_TO_SEVERITY_NUMBER = {
24
+ debug: 5,
25
+ info: 9,
26
+ warn: 13,
27
+ error: 17,
28
+ fatal: 21
29
+ };
30
+ var LEVEL_TO_SEVERITY_TEXT = {
31
+ debug: "DEBUG",
32
+ info: "INFO",
33
+ warn: "WARN",
34
+ error: "ERROR",
35
+ fatal: "FATAL"
36
+ };
37
+ function nowNs() {
38
+ return BigInt(Date.now()) * 1000000n;
39
+ }
40
+ function isValidTraceparentMatch(parsed) {
41
+ const version = parsed[1];
42
+ const traceId = parsed[2];
43
+ const spanId = parsed[3];
44
+ return version !== "ff" && traceId !== ZERO_TRACE_ID && spanId !== ZERO_SPAN_ID;
45
+ }
46
+ function randomSpanIdHex() {
47
+ if (typeof globalThis.crypto !== "undefined" && globalThis.crypto.getRandomValues) {
48
+ const bytes = new Uint8Array(8);
49
+ globalThis.crypto.getRandomValues(bytes);
50
+ let out2 = "";
51
+ for (const b of bytes) out2 += b.toString(16).padStart(2, "0");
52
+ return out2;
53
+ }
54
+ let out = "";
55
+ for (let i = 0; i < 16; i++) out += Math.floor(Math.random() * 16).toString(16);
56
+ return out;
57
+ }
58
+ function attributesToOtlp(attrs) {
59
+ const out = [];
60
+ for (const [key, raw] of Object.entries(attrs)) {
61
+ if (raw === void 0 || raw === null) continue;
62
+ if (typeof raw === "string") {
63
+ out.push({ key, value: { stringValue: raw } });
64
+ } else if (typeof raw === "boolean") {
65
+ out.push({ key, value: { boolValue: raw } });
66
+ } else if (typeof raw === "number") {
67
+ if (Number.isInteger(raw)) {
68
+ out.push({ key, value: { intValue: raw } });
69
+ } else {
70
+ out.push({ key, value: { doubleValue: raw } });
71
+ }
72
+ } else {
73
+ try {
74
+ out.push({ key, value: { stringValue: JSON.stringify(raw) } });
75
+ } catch {
76
+ }
77
+ }
78
+ }
79
+ return out;
80
+ }
81
+ var ObservabilityCollectorImpl = class {
82
+ parentContext;
83
+ #traceId;
84
+ #rootSpanId;
85
+ #spans = [];
86
+ #logs = [];
87
+ #spanStack = [];
88
+ /** Wall-clock execution timing, captured by withContext. */
89
+ #executionStartMs;
90
+ #executionEndMs;
91
+ #flushed = false;
92
+ constructor(parentContext) {
93
+ this.parentContext = parentContext;
94
+ const parsed = TRACEPARENT_RE.exec(parentContext.traceparent);
95
+ if (!parsed || !isValidTraceparentMatch(parsed)) {
96
+ this.#traceId = ZERO_TRACE_ID;
97
+ this.#rootSpanId = ZERO_SPAN_ID;
98
+ return;
99
+ }
100
+ this.#traceId = parsed[2];
101
+ this.#rootSpanId = parsed[3];
102
+ }
103
+ async withContext(fn) {
104
+ const executionStartMs = this.#executionStartMs ?? Date.now();
105
+ this.#executionStartMs = executionStartMs;
106
+ this.#spanStack.push(this.#rootSpanId);
107
+ try {
108
+ return await fn();
109
+ } finally {
110
+ this.#spanStack.pop();
111
+ this.#executionEndMs = Date.now();
112
+ }
113
+ }
114
+ async span(name, fn, attributes) {
115
+ const spanId = randomSpanIdHex();
116
+ const spanStack = this.#getSpanStack();
117
+ const parentSpanId = spanStack[spanStack.length - 1] ?? this.#rootSpanId;
118
+ const startTimeNs = nowNs();
119
+ spanStack.push(spanId);
120
+ let statusCode = 1;
121
+ let statusMessage;
122
+ try {
123
+ return await fn();
124
+ } catch (err) {
125
+ statusCode = 2;
126
+ statusMessage = err instanceof Error ? err.message : String(err);
127
+ throw err;
128
+ } finally {
129
+ spanStack.pop();
130
+ this.#spans.push({
131
+ spanId,
132
+ parentSpanId,
133
+ traceId: this.#traceId,
134
+ name,
135
+ startTimeNs,
136
+ endTimeNs: nowNs(),
137
+ attributes: attributes ?? {},
138
+ statusCode,
139
+ statusMessage
140
+ });
141
+ }
142
+ }
143
+ log(level, message, data) {
144
+ const spanStack = this.#getSpanStack();
145
+ const spanId = spanStack[spanStack.length - 1] ?? this.#rootSpanId;
146
+ this.#logs.push({
147
+ spanId,
148
+ traceId: this.#traceId,
149
+ timestampNs: nowNs(),
150
+ level,
151
+ message,
152
+ data
153
+ });
154
+ }
155
+ #getSpanStack() {
156
+ return this.#spanStack;
157
+ }
158
+ flush() {
159
+ if (this.#flushed) {
160
+ return {};
161
+ }
162
+ this.#flushed = true;
163
+ const payload = {};
164
+ if (this.#executionStartMs !== void 0 && this.#executionEndMs !== void 0) {
165
+ payload.executionDurationMs = this.#executionEndMs - this.#executionStartMs;
166
+ }
167
+ if (this.#spans.length > 0) {
168
+ payload.spans = {
169
+ resourceSpans: [
170
+ {
171
+ scopeSpans: [
172
+ {
173
+ scope: { name: "@mastra/client-js/observability", version: "1" },
174
+ spans: this.#spans.map((s) => ({
175
+ traceId: s.traceId,
176
+ spanId: s.spanId,
177
+ parentSpanId: s.parentSpanId,
178
+ name: s.name,
179
+ // OTLP/JSON allows nanosecond timestamps as decimal
180
+ // strings to avoid JS number precision loss for
181
+ // uint64. Use string form.
182
+ startTimeUnixNano: s.startTimeNs.toString(),
183
+ endTimeUnixNano: s.endTimeNs.toString(),
184
+ attributes: attributesToOtlp(s.attributes),
185
+ status: { code: s.statusCode, ...s.statusMessage ? { message: s.statusMessage } : {} },
186
+ // OTLP requires `kind`; default to INTERNAL = 1.
187
+ kind: 1
188
+ }))
189
+ }
190
+ ]
191
+ }
192
+ ]
193
+ };
194
+ }
195
+ if (this.#logs.length > 0) {
196
+ payload.logs = {
197
+ resourceLogs: [
198
+ {
199
+ scopeLogs: [
200
+ {
201
+ scope: { name: "@mastra/client-js/observability", version: "1" },
202
+ logRecords: this.#logs.map((l) => ({
203
+ timeUnixNano: l.timestampNs.toString(),
204
+ observedTimeUnixNano: l.timestampNs.toString(),
205
+ severityNumber: LEVEL_TO_SEVERITY_NUMBER[l.level],
206
+ severityText: LEVEL_TO_SEVERITY_TEXT[l.level],
207
+ body: { stringValue: l.message },
208
+ traceId: l.traceId,
209
+ spanId: l.spanId,
210
+ attributes: l.data ? attributesToOtlp(l.data) : []
211
+ }))
212
+ }
213
+ ]
214
+ }
215
+ ]
216
+ };
217
+ }
218
+ return payload;
219
+ }
220
+ };
221
+ var createObservabilityCollector = (parentContext) => new ObservabilityCollectorImpl(parentContext);
18
222
  function normalizeRoutePath(path) {
19
223
  let normalized = path.trim();
20
224
  if (normalized.includes("..") || normalized.includes("?") || normalized.includes("#")) {
@@ -295,6 +499,49 @@ var BaseResource = class {
295
499
  };
296
500
 
297
501
  // src/resources/agent.ts
502
+ var noopClientToolObserve = {
503
+ async span(_name, fn) {
504
+ return fn();
505
+ },
506
+ log() {
507
+ }
508
+ };
509
+ function getClientToolObservabilityContext(toolCall) {
510
+ const candidate = toolCall;
511
+ return candidate?.payload?.observability ?? candidate?.observability;
512
+ }
513
+ async function executeClientToolWithObservability({
514
+ clientTool,
515
+ args,
516
+ toolName,
517
+ parentContext,
518
+ executeContext
519
+ }) {
520
+ const collector = parentContext ? createObservabilityCollector(parentContext) : void 0;
521
+ const observe = collector ? {
522
+ span: collector.span.bind(collector),
523
+ log: collector.log.bind(collector)
524
+ } : noopClientToolObserve;
525
+ const runExecute = () => clientTool.execute(args, {
526
+ ...executeContext,
527
+ observe
528
+ });
529
+ const result = collector ? await collector.withContext(runExecute) : await runExecute();
530
+ if (!parentContext) {
531
+ return { result };
532
+ }
533
+ const flushed = collector?.flush();
534
+ if (flushed) {
535
+ flushed.toolName = toolName;
536
+ }
537
+ return {
538
+ result,
539
+ observability: {
540
+ parentContext,
541
+ ...flushed ? { payload: flushed } : {}
542
+ }
543
+ };
544
+ }
298
545
  async function executeToolCallAndRespond({
299
546
  response,
300
547
  params,
@@ -312,31 +559,39 @@ async function executeToolCallAndRespond({
312
559
  for (const toolCall of toolCalls) {
313
560
  const clientTool = params.clientTools?.[toolCall.payload.toolName];
314
561
  if (clientTool && clientTool.execute) {
315
- const result = await clientTool.execute(toolCall?.payload.args, {
316
- requestContext,
317
- tracingContext: { currentSpan: void 0 },
318
- agent: {
319
- agentId,
320
- messages: response.messages,
321
- toolCallId: toolCall?.payload.toolCallId,
322
- suspend: async () => {
323
- },
324
- threadId,
325
- resourceId
562
+ const { result, observability } = await executeClientToolWithObservability({
563
+ clientTool,
564
+ args: toolCall?.payload.args,
565
+ toolName: toolCall.payload.toolName,
566
+ parentContext: getClientToolObservabilityContext(toolCall),
567
+ executeContext: {
568
+ requestContext,
569
+ tracingContext: { currentSpan: void 0 },
570
+ agent: {
571
+ agentId,
572
+ messages: response.messages,
573
+ toolCallId: toolCall?.payload.toolCallId,
574
+ suspend: async () => {
575
+ },
576
+ threadId,
577
+ resourceId
578
+ }
326
579
  }
327
580
  });
581
+ const toolResultContent = {
582
+ type: "tool-result",
583
+ toolCallId: toolCall.payload.toolCallId,
584
+ toolName: toolCall.payload.toolName,
585
+ result
586
+ };
587
+ if (observability) {
588
+ toolResultContent.__mastraObservability = observability;
589
+ }
328
590
  const newMessages = [
329
591
  ...response.response.messages || [],
330
592
  {
331
593
  role: "tool",
332
- content: [
333
- {
334
- type: "tool-result",
335
- toolCallId: toolCall.payload.toolCallId,
336
- toolName: toolCall.payload.toolName,
337
- result
338
- }
339
- ]
594
+ content: [toolResultContent]
340
595
  }
341
596
  ];
342
597
  const updatedMessages = threadId ? newMessages : [...Array.isArray(params.messages) ? params.messages : [], ...newMessages];
@@ -659,17 +914,23 @@ var Agent = class extends BaseResource {
659
914
  for (const toolCall of toolCalls) {
660
915
  const clientTool = params.clientTools?.[toolCall.toolName];
661
916
  if (clientTool && clientTool.execute) {
662
- const result = await clientTool.execute(toolCall?.args, {
663
- requestContext,
664
- tracingContext: { currentSpan: void 0 },
665
- agent: {
666
- agentId: this.agentId,
667
- messages: response.messages,
668
- toolCallId: toolCall?.toolCallId,
669
- suspend: async () => {
670
- },
671
- threadId,
672
- resourceId
917
+ const { result, observability } = await executeClientToolWithObservability({
918
+ clientTool,
919
+ args: toolCall?.args,
920
+ toolName: toolCall.toolName,
921
+ parentContext: getClientToolObservabilityContext(toolCall),
922
+ executeContext: {
923
+ requestContext,
924
+ tracingContext: { currentSpan: void 0 },
925
+ agent: {
926
+ agentId: this.agentId,
927
+ messages: response.messages,
928
+ toolCallId: toolCall?.toolCallId,
929
+ suspend: async () => {
930
+ },
931
+ threadId,
932
+ resourceId
933
+ }
673
934
  }
674
935
  });
675
936
  const updatedMessages = [
@@ -681,7 +942,8 @@ var Agent = class extends BaseResource {
681
942
  type: "tool-result",
682
943
  toolCallId: toolCall.toolCallId,
683
944
  toolName: toolCall.toolName,
684
- result
945
+ result,
946
+ ...observability ? { __mastraObservability: observability } : {}
685
947
  }
686
948
  ]
687
949
  }
@@ -881,14 +1143,17 @@ var Agent = class extends BaseResource {
881
1143
  text: "",
882
1144
  step,
883
1145
  toolName: value.toolName,
884
- index: message.toolInvocations.length
1146
+ index: message.toolInvocations.length,
1147
+ observability: getClientToolObservabilityContext(value)
885
1148
  };
1149
+ const observability = getClientToolObservabilityContext(value);
886
1150
  const invocation = {
887
1151
  state: "partial-call",
888
1152
  step,
889
1153
  toolCallId: value.toolCallId,
890
1154
  toolName: value.toolName,
891
- args: void 0
1155
+ args: void 0,
1156
+ ...observability ? { observability } : {}
892
1157
  };
893
1158
  message.toolInvocations.push(invocation);
894
1159
  updateToolInvocationPart(value.toolCallId, invocation);
@@ -903,7 +1168,8 @@ var Agent = class extends BaseResource {
903
1168
  step: partialToolCall.step,
904
1169
  toolCallId: value.toolCallId,
905
1170
  toolName: partialToolCall.toolName,
906
- args: partialArgs
1171
+ args: partialArgs,
1172
+ ...partialToolCall.observability ? { observability: partialToolCall.observability } : {}
907
1173
  };
908
1174
  message.toolInvocations[partialToolCall.index] = invocation;
909
1175
  updateToolInvocationPart(value.toolCallId, invocation);
@@ -1205,14 +1471,17 @@ var Agent = class extends BaseResource {
1205
1471
  text: "",
1206
1472
  step,
1207
1473
  toolName: chunk.payload.toolName,
1208
- index: message.toolInvocations.length
1474
+ index: message.toolInvocations.length,
1475
+ observability: getClientToolObservabilityContext(chunk)
1209
1476
  };
1477
+ const observability = getClientToolObservabilityContext(chunk);
1210
1478
  const invocation = {
1211
1479
  state: "partial-call",
1212
1480
  step,
1213
1481
  toolCallId: chunk.payload.toolCallId,
1214
1482
  toolName: chunk.payload.toolName,
1215
- args: chunk.payload.args
1483
+ args: chunk.payload.args,
1484
+ ...observability ? { observability } : {}
1216
1485
  };
1217
1486
  message.toolInvocations.push(invocation);
1218
1487
  updateToolInvocationPart(chunk.payload.toolCallId, invocation);
@@ -1228,7 +1497,8 @@ var Agent = class extends BaseResource {
1228
1497
  step: partialToolCall.step,
1229
1498
  toolCallId: chunk.payload.toolCallId,
1230
1499
  toolName: partialToolCall.toolName,
1231
- args: partialArgs
1500
+ args: partialArgs,
1501
+ ...partialToolCall.observability ? { observability: partialToolCall.observability } : {}
1232
1502
  };
1233
1503
  message.toolInvocations[partialToolCall.index] = invocation;
1234
1504
  updateToolInvocationPart(chunk.payload.toolCallId, invocation);
@@ -1347,25 +1617,37 @@ var Agent = class extends BaseResource {
1347
1617
  if (finishReason === "tool-calls") {
1348
1618
  const toolCall = [...message?.parts ?? []].reverse().find((part) => part.type === "tool-invocation")?.toolInvocation;
1349
1619
  if (toolCall) {
1350
- toolCalls.push(toolCall);
1620
+ const toolInvocationWithMetadata = message?.toolInvocations?.find(
1621
+ (invocation) => invocation.toolCallId === toolCall.toolCallId
1622
+ );
1623
+ toolCalls.push({
1624
+ ...toolInvocationWithMetadata,
1625
+ ...toolCall,
1626
+ ...!getClientToolObservabilityContext(toolCall) && toolInvocationWithMetadata ? { observability: getClientToolObservabilityContext(toolInvocationWithMetadata) } : {}
1627
+ });
1351
1628
  }
1352
1629
  let shouldExecuteClientTool = false;
1353
1630
  for (const toolCall2 of toolCalls) {
1354
1631
  const clientTool = processedParams.clientTools?.[toolCall2.toolName];
1355
1632
  if (clientTool && clientTool.execute) {
1356
1633
  shouldExecuteClientTool = true;
1357
- const result = await clientTool.execute(toolCall2?.args, {
1358
- requestContext: processedParams.requestContext,
1359
- // TODO: Pass proper tracing context when client-js supports tracing
1360
- tracingContext: { currentSpan: void 0 },
1361
- agent: {
1362
- agentId: this.agentId,
1363
- messages: response.messages,
1364
- toolCallId: toolCall2?.toolCallId,
1365
- suspend: async () => {
1366
- },
1367
- threadId,
1368
- resourceId
1634
+ const { result, observability } = await executeClientToolWithObservability({
1635
+ clientTool,
1636
+ args: toolCall2?.args,
1637
+ toolName: toolCall2.toolName,
1638
+ parentContext: getClientToolObservabilityContext(toolCall2),
1639
+ executeContext: {
1640
+ requestContext: processedParams.requestContext,
1641
+ tracingContext: { currentSpan: void 0 },
1642
+ agent: {
1643
+ agentId: this.agentId,
1644
+ messages: response.messages,
1645
+ toolCallId: toolCall2?.toolCallId,
1646
+ suspend: async () => {
1647
+ },
1648
+ threadId,
1649
+ resourceId
1650
+ }
1369
1651
  }
1370
1652
  });
1371
1653
  const lastMessageRaw = messages[messages.length - 1];
@@ -1377,7 +1659,8 @@ var Agent = class extends BaseResource {
1377
1659
  toolInvocationPart.toolInvocation = {
1378
1660
  ...toolInvocationPart.toolInvocation,
1379
1661
  state: "result",
1380
- result
1662
+ result,
1663
+ ...observability ? { __mastraObservability: observability } : {}
1381
1664
  };
1382
1665
  }
1383
1666
  const toolInvocation = lastMessage?.toolInvocations?.find(
@@ -1386,6 +1669,9 @@ var Agent = class extends BaseResource {
1386
1669
  if (toolInvocation) {
1387
1670
  toolInvocation.state = "result";
1388
1671
  toolInvocation.result = result;
1672
+ if (observability) {
1673
+ toolInvocation.__mastraObservability = observability;
1674
+ }
1389
1675
  }
1390
1676
  const newMessages = lastMessage != null ? [...messages.filter((m) => m.id !== lastMessage.id), lastMessage] : [...messages];
1391
1677
  const updatedMessages = threadId ? newMessages : [...Array.isArray(processedParams.messages) ? processedParams.messages : [], ...newMessages];
@@ -1820,23 +2106,35 @@ var Agent = class extends BaseResource {
1820
2106
  if (finishReason === "tool-calls") {
1821
2107
  const toolCall = [...message?.parts ?? []].reverse().find((part) => part.type === "tool-invocation")?.toolInvocation;
1822
2108
  if (toolCall) {
1823
- toolCalls.push(toolCall);
2109
+ const toolInvocationWithMetadata = message?.toolInvocations?.find(
2110
+ (invocation) => invocation.toolCallId === toolCall.toolCallId
2111
+ );
2112
+ toolCalls.push({
2113
+ ...toolInvocationWithMetadata,
2114
+ ...toolCall,
2115
+ ...!getClientToolObservabilityContext(toolCall) && toolInvocationWithMetadata ? { observability: getClientToolObservabilityContext(toolInvocationWithMetadata) } : {}
2116
+ });
1824
2117
  }
1825
2118
  for (const toolCall2 of toolCalls) {
1826
2119
  const clientTool = processedParams.clientTools?.[toolCall2.toolName];
1827
2120
  if (clientTool && clientTool.execute) {
1828
- const result = await clientTool.execute(toolCall2?.args, {
1829
- requestContext: processedParams.requestContext,
1830
- // TODO: Pass proper tracing context when client-js supports tracing
1831
- tracingContext: { currentSpan: void 0 },
1832
- agent: {
1833
- agentId: this.agentId,
1834
- messages: response.messages,
1835
- toolCallId: toolCall2?.toolCallId,
1836
- suspend: async () => {
1837
- },
1838
- threadId,
1839
- resourceId
2121
+ const { result, observability } = await executeClientToolWithObservability({
2122
+ clientTool,
2123
+ args: toolCall2?.args,
2124
+ toolName: toolCall2.toolName,
2125
+ parentContext: getClientToolObservabilityContext(toolCall2),
2126
+ executeContext: {
2127
+ requestContext: processedParams.requestContext,
2128
+ tracingContext: { currentSpan: void 0 },
2129
+ agent: {
2130
+ agentId: this.agentId,
2131
+ messages: response.messages,
2132
+ toolCallId: toolCall2?.toolCallId,
2133
+ suspend: async () => {
2134
+ },
2135
+ threadId,
2136
+ resourceId
2137
+ }
1840
2138
  }
1841
2139
  });
1842
2140
  const lastMessage = JSON.parse(JSON.stringify(messages[messages.length - 1]));
@@ -1847,7 +2145,8 @@ var Agent = class extends BaseResource {
1847
2145
  toolInvocationPart.toolInvocation = {
1848
2146
  ...toolInvocationPart.toolInvocation,
1849
2147
  state: "result",
1850
- result
2148
+ result,
2149
+ ...observability ? { __mastraObservability: observability } : {}
1851
2150
  };
1852
2151
  }
1853
2152
  const toolInvocation = lastMessage?.toolInvocations?.find(
@@ -1856,6 +2155,9 @@ var Agent = class extends BaseResource {
1856
2155
  if (toolInvocation) {
1857
2156
  toolInvocation.state = "result";
1858
2157
  toolInvocation.result = result;
2158
+ if (observability) {
2159
+ toolInvocation.__mastraObservability = observability;
2160
+ }
1859
2161
  }
1860
2162
  const writer = writable.getWriter();
1861
2163
  try {