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

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,67 @@ var BaseResource = class {
295
499
  };
296
500
 
297
501
  // src/resources/agent.ts
502
+ var SIGNAL_RUNTIME_OPTIONS_TTL_MS = 5 * 60 * 1e3;
503
+ var signalRuntimeOptionsByRunId = /* @__PURE__ */ new Map();
504
+ var latestSignalRuntimeOptionsByThread = /* @__PURE__ */ new Map();
505
+ var createSignalRuntimeOptionsEntry = (store, key, streamOptions) => {
506
+ const timeout = setTimeout(() => store.delete(key), SIGNAL_RUNTIME_OPTIONS_TTL_MS);
507
+ timeout.unref?.();
508
+ return { streamOptions, timeout };
509
+ };
510
+ var setSignalRuntimeOptionsEntry = (store, key, streamOptions) => {
511
+ const existing = store.get(key);
512
+ if (existing) clearTimeout(existing.timeout);
513
+ store.set(key, createSignalRuntimeOptionsEntry(store, key, streamOptions));
514
+ };
515
+ var deleteSignalRuntimeOptionsEntry = (store, key) => {
516
+ const existing = store.get(key);
517
+ if (existing) clearTimeout(existing.timeout);
518
+ store.delete(key);
519
+ };
520
+ var noopClientToolObserve = {
521
+ async span(_name, fn) {
522
+ return fn();
523
+ },
524
+ log() {
525
+ }
526
+ };
527
+ function getClientToolObservabilityContext(toolCall) {
528
+ const candidate = toolCall;
529
+ return candidate?.payload?.observability ?? candidate?.observability;
530
+ }
531
+ async function executeClientToolWithObservability({
532
+ clientTool,
533
+ args,
534
+ toolName,
535
+ parentContext,
536
+ executeContext
537
+ }) {
538
+ const collector = parentContext ? createObservabilityCollector(parentContext) : void 0;
539
+ const observe = collector ? {
540
+ span: collector.span.bind(collector),
541
+ log: collector.log.bind(collector)
542
+ } : noopClientToolObserve;
543
+ const runExecute = () => clientTool.execute(args, {
544
+ ...executeContext,
545
+ observe
546
+ });
547
+ const result = collector ? await collector.withContext(runExecute) : await runExecute();
548
+ if (!parentContext) {
549
+ return { result };
550
+ }
551
+ const flushed = collector?.flush();
552
+ if (flushed) {
553
+ flushed.toolName = toolName;
554
+ }
555
+ return {
556
+ result,
557
+ observability: {
558
+ parentContext,
559
+ ...flushed ? { payload: flushed } : {}
560
+ }
561
+ };
562
+ }
298
563
  async function executeToolCallAndRespond({
299
564
  response,
300
565
  params,
@@ -312,31 +577,39 @@ async function executeToolCallAndRespond({
312
577
  for (const toolCall of toolCalls) {
313
578
  const clientTool = params.clientTools?.[toolCall.payload.toolName];
314
579
  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
580
+ const { result, observability } = await executeClientToolWithObservability({
581
+ clientTool,
582
+ args: toolCall?.payload.args,
583
+ toolName: toolCall.payload.toolName,
584
+ parentContext: getClientToolObservabilityContext(toolCall),
585
+ executeContext: {
586
+ requestContext,
587
+ tracingContext: { currentSpan: void 0 },
588
+ agent: {
589
+ agentId,
590
+ messages: response.messages,
591
+ toolCallId: toolCall?.payload.toolCallId,
592
+ suspend: async () => {
593
+ },
594
+ threadId,
595
+ resourceId
596
+ }
326
597
  }
327
598
  });
599
+ const toolResultContent = {
600
+ type: "tool-result",
601
+ toolCallId: toolCall.payload.toolCallId,
602
+ toolName: toolCall.payload.toolName,
603
+ result
604
+ };
605
+ if (observability) {
606
+ toolResultContent.__mastraObservability = observability;
607
+ }
328
608
  const newMessages = [
329
609
  ...response.response.messages || [],
330
610
  {
331
611
  role: "tool",
332
- content: [
333
- {
334
- type: "tool-result",
335
- toolCallId: toolCall.payload.toolCallId,
336
- toolName: toolCall.payload.toolName,
337
- result
338
- }
339
- ]
612
+ content: [toolResultContent]
340
613
  }
341
614
  ];
342
615
  const updatedMessages = threadId ? newMessages : [...Array.isArray(params.messages) ? params.messages : [], ...newMessages];
@@ -441,6 +714,52 @@ var Agent = class extends BaseResource {
441
714
  const queryString = searchParams.toString();
442
715
  return queryString ? `${delimiter}${queryString}` : "";
443
716
  }
717
+ getSignalRuntimeRunKey(runId) {
718
+ return `${this.options.baseUrl}|${this.apiPrefix}|${this.agentId}|${runId}`;
719
+ }
720
+ getSignalRuntimeThreadKey({
721
+ resourceId,
722
+ threadId
723
+ }) {
724
+ if (!threadId) return void 0;
725
+ return `${this.options.baseUrl}|${this.apiPrefix}|${this.agentId}|${resourceId ?? ""}|${threadId}`;
726
+ }
727
+ setSignalRuntimeOptions({
728
+ runId,
729
+ resourceId,
730
+ threadId,
731
+ streamOptions
732
+ }) {
733
+ const threadKey = this.getSignalRuntimeThreadKey({ resourceId, threadId });
734
+ if (runId) {
735
+ setSignalRuntimeOptionsEntry(signalRuntimeOptionsByRunId, this.getSignalRuntimeRunKey(runId), streamOptions);
736
+ if (threadKey) deleteSignalRuntimeOptionsEntry(latestSignalRuntimeOptionsByThread, threadKey);
737
+ return;
738
+ }
739
+ if (threadKey) {
740
+ setSignalRuntimeOptionsEntry(latestSignalRuntimeOptionsByThread, threadKey, streamOptions);
741
+ }
742
+ }
743
+ getSignalRuntimeOptions({
744
+ runId,
745
+ resourceId,
746
+ threadId
747
+ }) {
748
+ if (runId) {
749
+ const runOptions = signalRuntimeOptionsByRunId.get(this.getSignalRuntimeRunKey(runId));
750
+ if (runOptions) return runOptions.streamOptions;
751
+ }
752
+ const threadKey = this.getSignalRuntimeThreadKey({ resourceId, threadId });
753
+ return threadKey ? latestSignalRuntimeOptionsByThread.get(threadKey)?.streamOptions : void 0;
754
+ }
755
+ deleteSignalRuntimeOptions(runId) {
756
+ if (!runId) return;
757
+ deleteSignalRuntimeOptionsEntry(signalRuntimeOptionsByRunId, this.getSignalRuntimeRunKey(runId));
758
+ }
759
+ deleteLatestSignalRuntimeOptions({ resourceId, threadId }) {
760
+ const threadKey = this.getSignalRuntimeThreadKey({ resourceId, threadId });
761
+ if (threadKey) deleteSignalRuntimeOptionsEntry(latestSignalRuntimeOptionsByThread, threadKey);
762
+ }
444
763
  /**
445
764
  * Retrieves details about the agent
446
765
  * @param requestContext - Optional request context to pass as query parameter
@@ -486,32 +805,227 @@ var Agent = class extends BaseResource {
486
805
  /**
487
806
  * @experimental Agent signals are experimental and may change in a future release.
488
807
  */
489
- sendSignal(params) {
490
- return this.request(`/agents/${this.agentId}/signals`, {
491
- method: "POST",
492
- body: params
493
- });
808
+ async sendSignal(params) {
809
+ const streamOptions = params.ifIdle?.streamOptions;
810
+ if (streamOptions) {
811
+ this.setSignalRuntimeOptions({
812
+ resourceId: params.resourceId,
813
+ threadId: params.threadId,
814
+ streamOptions
815
+ });
816
+ }
817
+ const body = params.ifIdle?.streamOptions ? {
818
+ ...params,
819
+ ifIdle: {
820
+ ...params.ifIdle,
821
+ streamOptions: {
822
+ ...params.ifIdle.streamOptions,
823
+ requestContext: parseClientRequestContext(params.ifIdle.streamOptions.requestContext),
824
+ clientTools: processClientTools(params.ifIdle.streamOptions.clientTools)
825
+ }
826
+ }
827
+ } : params;
828
+ let response;
829
+ try {
830
+ response = await this.request(`/agents/${this.agentId}/signals`, {
831
+ method: "POST",
832
+ body
833
+ });
834
+ } catch (error) {
835
+ if (streamOptions) {
836
+ this.deleteLatestSignalRuntimeOptions({ resourceId: params.resourceId, threadId: params.threadId });
837
+ }
838
+ throw error;
839
+ }
840
+ if (streamOptions) {
841
+ this.setSignalRuntimeOptions({
842
+ runId: response.runId,
843
+ resourceId: params.resourceId,
844
+ threadId: params.threadId,
845
+ streamOptions
846
+ });
847
+ }
848
+ return response;
494
849
  }
495
850
  /**
496
851
  * @experimental Agent signals are experimental and may change in a future release.
497
852
  */
498
853
  async subscribeToThread(params) {
499
- const streamResponse = await this.request(`/agents/${this.agentId}/threads/subscribe`, {
854
+ const { resourceId, threadId } = params;
855
+ const requestSubscription = () => this.request(`/agents/${this.agentId}/threads/subscribe`, {
500
856
  method: "POST",
501
- body: params,
857
+ body: { resourceId, threadId },
502
858
  stream: true
503
859
  });
860
+ const streamResponse = await requestSubscription();
504
861
  if (!streamResponse.body) {
505
862
  throw new Error("No response body");
506
863
  }
507
- streamResponse.processDataStream = async ({
508
- onChunk
509
- }) => {
510
- await processMastraStream({
511
- stream: streamResponse.body,
512
- onChunk,
513
- signal: this.options.abortSignal
514
- });
864
+ const agent = this;
865
+ streamResponse.processDataStream = async ({ onChunk, reconnect }) => {
866
+ const pendingToolCallsByRunId = /* @__PURE__ */ new Map();
867
+ const handleSubscribedChunk = async (chunk) => {
868
+ if (chunk.type === "tool-call") {
869
+ const payload = chunk.payload;
870
+ const toolCallId = payload?.toolCallId;
871
+ const toolName = payload?.toolName;
872
+ const runId2 = chunk.runId;
873
+ if (!toolCallId || !toolName || !runId2) return;
874
+ const pendingToolCalls2 = pendingToolCallsByRunId.get(runId2) ?? [];
875
+ pendingToolCalls2.push({ toolCallId, toolName, args: payload.args, observability: payload.observability });
876
+ pendingToolCallsByRunId.set(runId2, pendingToolCalls2);
877
+ return;
878
+ }
879
+ if (chunk.type !== "finish") return;
880
+ const runId = chunk.runId;
881
+ const finishPayload = chunk;
882
+ if (!runId) return;
883
+ if (finishPayload.payload?.stepResult?.reason !== "tool-calls") {
884
+ agent.deleteSignalRuntimeOptions(runId);
885
+ return;
886
+ }
887
+ const pendingToolCalls = pendingToolCallsByRunId.get(runId);
888
+ pendingToolCallsByRunId.delete(runId);
889
+ if (!pendingToolCalls?.length) {
890
+ agent.deleteSignalRuntimeOptions(runId);
891
+ return;
892
+ }
893
+ const activeRuntimeOptions = agent.getSignalRuntimeOptions({ runId, resourceId, threadId });
894
+ const activeClientTools = activeRuntimeOptions?.clientTools;
895
+ if (!activeClientTools) {
896
+ agent.deleteSignalRuntimeOptions(runId);
897
+ return;
898
+ }
899
+ const activeRequestContext = activeRuntimeOptions.requestContext;
900
+ const processedClientTools = processClientTools(activeClientTools);
901
+ const processedRequestContext = parseClientRequestContext(activeRequestContext);
902
+ const toolResultMessages = [];
903
+ for (const toolCall of pendingToolCalls) {
904
+ const clientTool = activeClientTools[toolCall.toolName];
905
+ if (!clientTool || typeof clientTool.execute !== "function") continue;
906
+ let result;
907
+ let observability;
908
+ try {
909
+ const execution = await executeClientToolWithObservability({
910
+ clientTool,
911
+ args: toolCall.args,
912
+ toolName: toolCall.toolName,
913
+ parentContext: toolCall.observability,
914
+ executeContext: {
915
+ requestContext: activeRequestContext,
916
+ tracingContext: { currentSpan: void 0 },
917
+ agent: {
918
+ agentId: agent.agentId,
919
+ messages: finishPayload.payload?.messages?.nonUser ?? [],
920
+ toolCallId: toolCall.toolCallId,
921
+ suspend: async () => {
922
+ },
923
+ threadId,
924
+ resourceId
925
+ }
926
+ }
927
+ });
928
+ result = execution.result;
929
+ observability = execution.observability;
930
+ } catch (error) {
931
+ result = { error: String(error) };
932
+ }
933
+ const toolResultContent = {
934
+ type: "tool-result",
935
+ toolCallId: toolCall.toolCallId,
936
+ toolName: toolCall.toolName,
937
+ result
938
+ };
939
+ if (observability) {
940
+ toolResultContent.__mastraObservability = observability;
941
+ }
942
+ await onChunk({
943
+ type: "tool-result",
944
+ runId,
945
+ payload: toolResultContent
946
+ });
947
+ toolResultMessages.push({
948
+ role: "tool",
949
+ content: [toolResultContent]
950
+ });
951
+ }
952
+ if (toolResultMessages.length === 0) {
953
+ agent.deleteSignalRuntimeOptions(runId);
954
+ return;
955
+ }
956
+ try {
957
+ const continuation = await agent.streamUntilIdle(
958
+ [...finishPayload.payload?.messages?.nonUser ?? [], ...toolResultMessages],
959
+ {
960
+ ...activeRuntimeOptions,
961
+ runId: uuid.v4(),
962
+ requestContext: processedRequestContext,
963
+ memory: threadId ? { thread: threadId, resource: resourceId } : void 0,
964
+ clientTools: processedClientTools
965
+ }
966
+ );
967
+ try {
968
+ void continuation.body?.cancel?.();
969
+ } catch {
970
+ }
971
+ } catch (error) {
972
+ console.error("Error running client-tool continuation:", error);
973
+ } finally {
974
+ agent.deleteSignalRuntimeOptions(runId);
975
+ }
976
+ };
977
+ const reconnectOptions = reconnect === true ? { maxRetries: Infinity, delayMs: 1e3 } : reconnect ? { maxRetries: reconnect.maxRetries ?? Infinity, delayMs: reconnect.delayMs ?? 1e3 } : null;
978
+ let response = streamResponse;
979
+ let attempts = 0;
980
+ const onChunkErrorSentinel = /* @__PURE__ */ Symbol("onChunkErrorSentinel");
981
+ const guardedOnChunk = async (chunk) => {
982
+ try {
983
+ await onChunk(chunk);
984
+ await handleSubscribedChunk(chunk);
985
+ } catch (cause) {
986
+ throw { [onChunkErrorSentinel]: true, cause };
987
+ }
988
+ };
989
+ while (true) {
990
+ if (!response.body) {
991
+ throw new Error("No response body");
992
+ }
993
+ try {
994
+ await processMastraStream({
995
+ stream: response.body,
996
+ onChunk: guardedOnChunk,
997
+ signal: this.options.abortSignal
998
+ });
999
+ } catch (error) {
1000
+ if (typeof error === "object" && error !== null && error[onChunkErrorSentinel]) {
1001
+ throw error.cause;
1002
+ }
1003
+ if (!reconnectOptions || this.options.abortSignal?.aborted || attempts >= reconnectOptions.maxRetries) {
1004
+ throw error;
1005
+ }
1006
+ }
1007
+ if (!reconnectOptions || this.options.abortSignal?.aborted || attempts >= reconnectOptions.maxRetries) {
1008
+ return;
1009
+ }
1010
+ while (attempts < reconnectOptions.maxRetries) {
1011
+ attempts++;
1012
+ if (this.options.abortSignal?.aborted) {
1013
+ return;
1014
+ }
1015
+ await new Promise((resolve) => setTimeout(resolve, reconnectOptions.delayMs));
1016
+ if (this.options.abortSignal?.aborted) {
1017
+ return;
1018
+ }
1019
+ try {
1020
+ response = await requestSubscription();
1021
+ break;
1022
+ } catch (error) {
1023
+ if (this.options.abortSignal?.aborted || attempts >= reconnectOptions.maxRetries) {
1024
+ throw error;
1025
+ }
1026
+ }
1027
+ }
1028
+ }
515
1029
  };
516
1030
  return streamResponse;
517
1031
  }
@@ -659,17 +1173,23 @@ var Agent = class extends BaseResource {
659
1173
  for (const toolCall of toolCalls) {
660
1174
  const clientTool = params.clientTools?.[toolCall.toolName];
661
1175
  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
1176
+ const { result, observability } = await executeClientToolWithObservability({
1177
+ clientTool,
1178
+ args: toolCall?.args,
1179
+ toolName: toolCall.toolName,
1180
+ parentContext: getClientToolObservabilityContext(toolCall),
1181
+ executeContext: {
1182
+ requestContext,
1183
+ tracingContext: { currentSpan: void 0 },
1184
+ agent: {
1185
+ agentId: this.agentId,
1186
+ messages: response.messages,
1187
+ toolCallId: toolCall?.toolCallId,
1188
+ suspend: async () => {
1189
+ },
1190
+ threadId,
1191
+ resourceId
1192
+ }
673
1193
  }
674
1194
  });
675
1195
  const updatedMessages = [
@@ -681,7 +1201,8 @@ var Agent = class extends BaseResource {
681
1201
  type: "tool-result",
682
1202
  toolCallId: toolCall.toolCallId,
683
1203
  toolName: toolCall.toolName,
684
- result
1204
+ result,
1205
+ ...observability ? { __mastraObservability: observability } : {}
685
1206
  }
686
1207
  ]
687
1208
  }
@@ -881,14 +1402,17 @@ var Agent = class extends BaseResource {
881
1402
  text: "",
882
1403
  step,
883
1404
  toolName: value.toolName,
884
- index: message.toolInvocations.length
1405
+ index: message.toolInvocations.length,
1406
+ observability: getClientToolObservabilityContext(value)
885
1407
  };
1408
+ const observability = getClientToolObservabilityContext(value);
886
1409
  const invocation = {
887
1410
  state: "partial-call",
888
1411
  step,
889
1412
  toolCallId: value.toolCallId,
890
1413
  toolName: value.toolName,
891
- args: void 0
1414
+ args: void 0,
1415
+ ...observability ? { observability } : {}
892
1416
  };
893
1417
  message.toolInvocations.push(invocation);
894
1418
  updateToolInvocationPart(value.toolCallId, invocation);
@@ -903,7 +1427,8 @@ var Agent = class extends BaseResource {
903
1427
  step: partialToolCall.step,
904
1428
  toolCallId: value.toolCallId,
905
1429
  toolName: partialToolCall.toolName,
906
- args: partialArgs
1430
+ args: partialArgs,
1431
+ ...partialToolCall.observability ? { observability: partialToolCall.observability } : {}
907
1432
  };
908
1433
  message.toolInvocations[partialToolCall.index] = invocation;
909
1434
  updateToolInvocationPart(value.toolCallId, invocation);
@@ -1028,6 +1553,7 @@ var Agent = class extends BaseResource {
1028
1553
  update,
1029
1554
  onToolCall,
1030
1555
  onFinish,
1556
+ onStreamChunk,
1031
1557
  getCurrentDate = () => /* @__PURE__ */ new Date(),
1032
1558
  lastMessage
1033
1559
  }) {
@@ -1095,6 +1621,7 @@ var Agent = class extends BaseResource {
1095
1621
  // TODO: casting as any here because the stream types were all typed as any before in core.
1096
1622
  // but this is completely wrong and this fn is probably broken. Remove ":any" and you'll see a bunch of type errors
1097
1623
  onChunk: async (chunk) => {
1624
+ onStreamChunk?.(chunk);
1098
1625
  switch (chunk.type) {
1099
1626
  case "tripwire": {
1100
1627
  message.parts.push({
@@ -1196,6 +1723,7 @@ var Agent = class extends BaseResource {
1196
1723
  execUpdate();
1197
1724
  }
1198
1725
  }
1726
+ break;
1199
1727
  }
1200
1728
  case "tool-call-input-streaming-start": {
1201
1729
  if (message.toolInvocations == null) {
@@ -1205,14 +1733,17 @@ var Agent = class extends BaseResource {
1205
1733
  text: "",
1206
1734
  step,
1207
1735
  toolName: chunk.payload.toolName,
1208
- index: message.toolInvocations.length
1736
+ index: message.toolInvocations.length,
1737
+ observability: getClientToolObservabilityContext(chunk)
1209
1738
  };
1739
+ const observability = getClientToolObservabilityContext(chunk);
1210
1740
  const invocation = {
1211
1741
  state: "partial-call",
1212
1742
  step,
1213
1743
  toolCallId: chunk.payload.toolCallId,
1214
1744
  toolName: chunk.payload.toolName,
1215
- args: chunk.payload.args
1745
+ args: chunk.payload.args,
1746
+ ...observability ? { observability } : {}
1216
1747
  };
1217
1748
  message.toolInvocations.push(invocation);
1218
1749
  updateToolInvocationPart(chunk.payload.toolCallId, invocation);
@@ -1228,7 +1759,8 @@ var Agent = class extends BaseResource {
1228
1759
  step: partialToolCall.step,
1229
1760
  toolCallId: chunk.payload.toolCallId,
1230
1761
  toolName: partialToolCall.toolName,
1231
- args: partialArgs
1762
+ args: partialArgs,
1763
+ ...partialToolCall.observability ? { observability: partialToolCall.observability } : {}
1232
1764
  };
1233
1765
  message.toolInvocations[partialToolCall.index] = invocation;
1234
1766
  updateToolInvocationPart(chunk.payload.toolCallId, invocation);
@@ -1308,6 +1840,7 @@ var Agent = class extends BaseResource {
1308
1840
  try {
1309
1841
  let toolCalls = [];
1310
1842
  let messages = [];
1843
+ let streamRunId = processedParams.runId;
1311
1844
  const [streamForController, streamForProcessing] = response.body.tee();
1312
1845
  const pipePromise = streamForController.pipeTo(
1313
1846
  new WritableStream({
@@ -1347,27 +1880,90 @@ var Agent = class extends BaseResource {
1347
1880
  if (finishReason === "tool-calls") {
1348
1881
  const toolCall = [...message?.parts ?? []].reverse().find((part) => part.type === "tool-invocation")?.toolInvocation;
1349
1882
  if (toolCall) {
1350
- toolCalls.push(toolCall);
1883
+ const toolInvocationWithMetadata = message?.toolInvocations?.find(
1884
+ (invocation) => invocation.toolCallId === toolCall.toolCallId
1885
+ );
1886
+ toolCalls.push({
1887
+ ...toolInvocationWithMetadata,
1888
+ ...toolCall,
1889
+ ...!getClientToolObservabilityContext(toolCall) && toolInvocationWithMetadata ? { observability: getClientToolObservabilityContext(toolInvocationWithMetadata) } : {}
1890
+ });
1351
1891
  }
1352
1892
  let shouldExecuteClientTool = false;
1353
1893
  for (const toolCall2 of toolCalls) {
1354
1894
  const clientTool = processedParams.clientTools?.[toolCall2.toolName];
1355
1895
  if (clientTool && clientTool.execute) {
1356
1896
  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
1369
- }
1370
- });
1897
+ const runId = streamRunId ?? toolCall2.toolCallId;
1898
+ let result;
1899
+ let observability;
1900
+ let synthetic;
1901
+ try {
1902
+ ({ result, observability } = await executeClientToolWithObservability({
1903
+ clientTool,
1904
+ args: toolCall2?.args,
1905
+ toolName: toolCall2.toolName,
1906
+ parentContext: getClientToolObservabilityContext(toolCall2),
1907
+ executeContext: {
1908
+ requestContext: processedParams.requestContext,
1909
+ tracingContext: { currentSpan: void 0 },
1910
+ agent: {
1911
+ agentId: this.agentId,
1912
+ messages: response.messages,
1913
+ toolCallId: toolCall2?.toolCallId,
1914
+ suspend: async () => {
1915
+ },
1916
+ threadId,
1917
+ resourceId
1918
+ }
1919
+ }
1920
+ }));
1921
+ synthetic = {
1922
+ type: "tool-result",
1923
+ runId,
1924
+ from: "AGENT",
1925
+ payload: {
1926
+ toolCallId: toolCall2.toolCallId,
1927
+ toolName: toolCall2.toolName,
1928
+ result,
1929
+ isError: false,
1930
+ providerExecuted: false
1931
+ }
1932
+ };
1933
+ } catch (error) {
1934
+ synthetic = {
1935
+ type: "tool-error",
1936
+ runId,
1937
+ from: "AGENT",
1938
+ payload: {
1939
+ toolCallId: toolCall2.toolCallId,
1940
+ toolName: toolCall2.toolName,
1941
+ error,
1942
+ args: toolCall2?.args,
1943
+ providerExecuted: false
1944
+ }
1945
+ };
1946
+ result = { error: error instanceof Error ? error.message : String(error) };
1947
+ }
1948
+ try {
1949
+ await pipePromise;
1950
+ } catch {
1951
+ }
1952
+ try {
1953
+ const errorForSerialization = synthetic.type === "tool-error" ? synthetic.payload.error : void 0;
1954
+ const serializedError = errorForSerialization instanceof Error ? {
1955
+ name: errorForSerialization.name,
1956
+ message: errorForSerialization.message,
1957
+ stack: errorForSerialization.stack
1958
+ } : errorForSerialization;
1959
+ const payloadForWire = synthetic.type === "tool-error" ? { ...synthetic, payload: { ...synthetic.payload, error: serializedError } } : synthetic;
1960
+ const sseLine = `data: ${JSON.stringify(payloadForWire)}
1961
+
1962
+ `;
1963
+ controller.enqueue(new TextEncoder().encode(sseLine));
1964
+ } catch (enqueueErr) {
1965
+ console.error("Failed to enqueue synthetic tool-result chunk:", enqueueErr);
1966
+ }
1371
1967
  const lastMessageRaw = messages[messages.length - 1];
1372
1968
  const lastMessage = lastMessageRaw != null ? JSON.parse(JSON.stringify(lastMessageRaw)) : void 0;
1373
1969
  const toolInvocationPart = lastMessage?.parts?.find(
@@ -1377,7 +1973,8 @@ var Agent = class extends BaseResource {
1377
1973
  toolInvocationPart.toolInvocation = {
1378
1974
  ...toolInvocationPart.toolInvocation,
1379
1975
  state: "result",
1380
- result
1976
+ result,
1977
+ ...observability ? { __mastraObservability: observability } : {}
1381
1978
  };
1382
1979
  }
1383
1980
  const toolInvocation = lastMessage?.toolInvocations?.find(
@@ -1386,6 +1983,9 @@ var Agent = class extends BaseResource {
1386
1983
  if (toolInvocation) {
1387
1984
  toolInvocation.state = "result";
1388
1985
  toolInvocation.result = result;
1986
+ if (observability) {
1987
+ toolInvocation.__mastraObservability = observability;
1988
+ }
1389
1989
  }
1390
1990
  const newMessages = lastMessage != null ? [...messages.filter((m) => m.id !== lastMessage.id), lastMessage] : [...messages];
1391
1991
  const updatedMessages = threadId ? newMessages : [...Array.isArray(processedParams.messages) ? processedParams.messages : [], ...newMessages];
@@ -1413,6 +2013,11 @@ var Agent = class extends BaseResource {
1413
2013
  controller.close();
1414
2014
  }
1415
2015
  },
2016
+ onStreamChunk: (chunk) => {
2017
+ if (!streamRunId && typeof chunk.runId === "string") {
2018
+ streamRunId = chunk.runId;
2019
+ }
2020
+ },
1416
2021
  lastMessage: void 0
1417
2022
  }).catch(async (error) => {
1418
2023
  console.error("Error processing stream response:", error);
@@ -1820,23 +2425,35 @@ var Agent = class extends BaseResource {
1820
2425
  if (finishReason === "tool-calls") {
1821
2426
  const toolCall = [...message?.parts ?? []].reverse().find((part) => part.type === "tool-invocation")?.toolInvocation;
1822
2427
  if (toolCall) {
1823
- toolCalls.push(toolCall);
2428
+ const toolInvocationWithMetadata = message?.toolInvocations?.find(
2429
+ (invocation) => invocation.toolCallId === toolCall.toolCallId
2430
+ );
2431
+ toolCalls.push({
2432
+ ...toolInvocationWithMetadata,
2433
+ ...toolCall,
2434
+ ...!getClientToolObservabilityContext(toolCall) && toolInvocationWithMetadata ? { observability: getClientToolObservabilityContext(toolInvocationWithMetadata) } : {}
2435
+ });
1824
2436
  }
1825
2437
  for (const toolCall2 of toolCalls) {
1826
2438
  const clientTool = processedParams.clientTools?.[toolCall2.toolName];
1827
2439
  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
2440
+ const { result, observability } = await executeClientToolWithObservability({
2441
+ clientTool,
2442
+ args: toolCall2?.args,
2443
+ toolName: toolCall2.toolName,
2444
+ parentContext: getClientToolObservabilityContext(toolCall2),
2445
+ executeContext: {
2446
+ requestContext: processedParams.requestContext,
2447
+ tracingContext: { currentSpan: void 0 },
2448
+ agent: {
2449
+ agentId: this.agentId,
2450
+ messages: response.messages,
2451
+ toolCallId: toolCall2?.toolCallId,
2452
+ suspend: async () => {
2453
+ },
2454
+ threadId,
2455
+ resourceId
2456
+ }
1840
2457
  }
1841
2458
  });
1842
2459
  const lastMessage = JSON.parse(JSON.stringify(messages[messages.length - 1]));
@@ -1847,7 +2464,8 @@ var Agent = class extends BaseResource {
1847
2464
  toolInvocationPart.toolInvocation = {
1848
2465
  ...toolInvocationPart.toolInvocation,
1849
2466
  state: "result",
1850
- result
2467
+ result,
2468
+ ...observability ? { __mastraObservability: observability } : {}
1851
2469
  };
1852
2470
  }
1853
2471
  const toolInvocation = lastMessage?.toolInvocations?.find(
@@ -1856,6 +2474,9 @@ var Agent = class extends BaseResource {
1856
2474
  if (toolInvocation) {
1857
2475
  toolInvocation.state = "result";
1858
2476
  toolInvocation.result = result;
2477
+ if (observability) {
2478
+ toolInvocation.__mastraObservability = observability;
2479
+ }
1859
2480
  }
1860
2481
  const writer = writable.getWriter();
1861
2482
  try {