@mastra/client-js 1.21.0-alpha.7 → 1.21.0-alpha.8

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.js CHANGED
@@ -494,6 +494,24 @@ var BaseResource = class {
494
494
  };
495
495
 
496
496
  // src/resources/agent.ts
497
+ var SIGNAL_RUNTIME_OPTIONS_TTL_MS = 5 * 60 * 1e3;
498
+ var signalRuntimeOptionsByRunId = /* @__PURE__ */ new Map();
499
+ var latestSignalRuntimeOptionsByThread = /* @__PURE__ */ new Map();
500
+ var createSignalRuntimeOptionsEntry = (store, key, streamOptions) => {
501
+ const timeout = setTimeout(() => store.delete(key), SIGNAL_RUNTIME_OPTIONS_TTL_MS);
502
+ timeout.unref?.();
503
+ return { streamOptions, timeout };
504
+ };
505
+ var setSignalRuntimeOptionsEntry = (store, key, streamOptions) => {
506
+ const existing = store.get(key);
507
+ if (existing) clearTimeout(existing.timeout);
508
+ store.set(key, createSignalRuntimeOptionsEntry(store, key, streamOptions));
509
+ };
510
+ var deleteSignalRuntimeOptionsEntry = (store, key) => {
511
+ const existing = store.get(key);
512
+ if (existing) clearTimeout(existing.timeout);
513
+ store.delete(key);
514
+ };
497
515
  var noopClientToolObserve = {
498
516
  async span(_name, fn) {
499
517
  return fn();
@@ -691,6 +709,52 @@ var Agent = class extends BaseResource {
691
709
  const queryString = searchParams.toString();
692
710
  return queryString ? `${delimiter}${queryString}` : "";
693
711
  }
712
+ getSignalRuntimeRunKey(runId) {
713
+ return `${this.options.baseUrl}|${this.apiPrefix}|${this.agentId}|${runId}`;
714
+ }
715
+ getSignalRuntimeThreadKey({
716
+ resourceId,
717
+ threadId
718
+ }) {
719
+ if (!threadId) return void 0;
720
+ return `${this.options.baseUrl}|${this.apiPrefix}|${this.agentId}|${resourceId ?? ""}|${threadId}`;
721
+ }
722
+ setSignalRuntimeOptions({
723
+ runId,
724
+ resourceId,
725
+ threadId,
726
+ streamOptions
727
+ }) {
728
+ const threadKey = this.getSignalRuntimeThreadKey({ resourceId, threadId });
729
+ if (runId) {
730
+ setSignalRuntimeOptionsEntry(signalRuntimeOptionsByRunId, this.getSignalRuntimeRunKey(runId), streamOptions);
731
+ if (threadKey) deleteSignalRuntimeOptionsEntry(latestSignalRuntimeOptionsByThread, threadKey);
732
+ return;
733
+ }
734
+ if (threadKey) {
735
+ setSignalRuntimeOptionsEntry(latestSignalRuntimeOptionsByThread, threadKey, streamOptions);
736
+ }
737
+ }
738
+ getSignalRuntimeOptions({
739
+ runId,
740
+ resourceId,
741
+ threadId
742
+ }) {
743
+ if (runId) {
744
+ const runOptions = signalRuntimeOptionsByRunId.get(this.getSignalRuntimeRunKey(runId));
745
+ if (runOptions) return runOptions.streamOptions;
746
+ }
747
+ const threadKey = this.getSignalRuntimeThreadKey({ resourceId, threadId });
748
+ return threadKey ? latestSignalRuntimeOptionsByThread.get(threadKey)?.streamOptions : void 0;
749
+ }
750
+ deleteSignalRuntimeOptions(runId) {
751
+ if (!runId) return;
752
+ deleteSignalRuntimeOptionsEntry(signalRuntimeOptionsByRunId, this.getSignalRuntimeRunKey(runId));
753
+ }
754
+ deleteLatestSignalRuntimeOptions({ resourceId, threadId }) {
755
+ const threadKey = this.getSignalRuntimeThreadKey({ resourceId, threadId });
756
+ if (threadKey) deleteSignalRuntimeOptionsEntry(latestSignalRuntimeOptionsByThread, threadKey);
757
+ }
694
758
  /**
695
759
  * Retrieves details about the agent
696
760
  * @param requestContext - Optional request context to pass as query parameter
@@ -736,32 +800,227 @@ var Agent = class extends BaseResource {
736
800
  /**
737
801
  * @experimental Agent signals are experimental and may change in a future release.
738
802
  */
739
- sendSignal(params) {
740
- return this.request(`/agents/${this.agentId}/signals`, {
741
- method: "POST",
742
- body: params
743
- });
803
+ async sendSignal(params) {
804
+ const streamOptions = params.ifIdle?.streamOptions;
805
+ if (streamOptions) {
806
+ this.setSignalRuntimeOptions({
807
+ resourceId: params.resourceId,
808
+ threadId: params.threadId,
809
+ streamOptions
810
+ });
811
+ }
812
+ const body = params.ifIdle?.streamOptions ? {
813
+ ...params,
814
+ ifIdle: {
815
+ ...params.ifIdle,
816
+ streamOptions: {
817
+ ...params.ifIdle.streamOptions,
818
+ requestContext: parseClientRequestContext(params.ifIdle.streamOptions.requestContext),
819
+ clientTools: processClientTools(params.ifIdle.streamOptions.clientTools)
820
+ }
821
+ }
822
+ } : params;
823
+ let response;
824
+ try {
825
+ response = await this.request(`/agents/${this.agentId}/signals`, {
826
+ method: "POST",
827
+ body
828
+ });
829
+ } catch (error) {
830
+ if (streamOptions) {
831
+ this.deleteLatestSignalRuntimeOptions({ resourceId: params.resourceId, threadId: params.threadId });
832
+ }
833
+ throw error;
834
+ }
835
+ if (streamOptions) {
836
+ this.setSignalRuntimeOptions({
837
+ runId: response.runId,
838
+ resourceId: params.resourceId,
839
+ threadId: params.threadId,
840
+ streamOptions
841
+ });
842
+ }
843
+ return response;
744
844
  }
745
845
  /**
746
846
  * @experimental Agent signals are experimental and may change in a future release.
747
847
  */
748
848
  async subscribeToThread(params) {
749
- const streamResponse = await this.request(`/agents/${this.agentId}/threads/subscribe`, {
849
+ const { resourceId, threadId } = params;
850
+ const requestSubscription = () => this.request(`/agents/${this.agentId}/threads/subscribe`, {
750
851
  method: "POST",
751
- body: params,
852
+ body: { resourceId, threadId },
752
853
  stream: true
753
854
  });
855
+ const streamResponse = await requestSubscription();
754
856
  if (!streamResponse.body) {
755
857
  throw new Error("No response body");
756
858
  }
757
- streamResponse.processDataStream = async ({
758
- onChunk
759
- }) => {
760
- await processMastraStream({
761
- stream: streamResponse.body,
762
- onChunk,
763
- signal: this.options.abortSignal
764
- });
859
+ const agent = this;
860
+ streamResponse.processDataStream = async ({ onChunk, reconnect }) => {
861
+ const pendingToolCallsByRunId = /* @__PURE__ */ new Map();
862
+ const handleSubscribedChunk = async (chunk) => {
863
+ if (chunk.type === "tool-call") {
864
+ const payload = chunk.payload;
865
+ const toolCallId = payload?.toolCallId;
866
+ const toolName = payload?.toolName;
867
+ const runId2 = chunk.runId;
868
+ if (!toolCallId || !toolName || !runId2) return;
869
+ const pendingToolCalls2 = pendingToolCallsByRunId.get(runId2) ?? [];
870
+ pendingToolCalls2.push({ toolCallId, toolName, args: payload.args, observability: payload.observability });
871
+ pendingToolCallsByRunId.set(runId2, pendingToolCalls2);
872
+ return;
873
+ }
874
+ if (chunk.type !== "finish") return;
875
+ const runId = chunk.runId;
876
+ const finishPayload = chunk;
877
+ if (!runId) return;
878
+ if (finishPayload.payload?.stepResult?.reason !== "tool-calls") {
879
+ agent.deleteSignalRuntimeOptions(runId);
880
+ return;
881
+ }
882
+ const pendingToolCalls = pendingToolCallsByRunId.get(runId);
883
+ pendingToolCallsByRunId.delete(runId);
884
+ if (!pendingToolCalls?.length) {
885
+ agent.deleteSignalRuntimeOptions(runId);
886
+ return;
887
+ }
888
+ const activeRuntimeOptions = agent.getSignalRuntimeOptions({ runId, resourceId, threadId });
889
+ const activeClientTools = activeRuntimeOptions?.clientTools;
890
+ if (!activeClientTools) {
891
+ agent.deleteSignalRuntimeOptions(runId);
892
+ return;
893
+ }
894
+ const activeRequestContext = activeRuntimeOptions.requestContext;
895
+ const processedClientTools = processClientTools(activeClientTools);
896
+ const processedRequestContext = parseClientRequestContext(activeRequestContext);
897
+ const toolResultMessages = [];
898
+ for (const toolCall of pendingToolCalls) {
899
+ const clientTool = activeClientTools[toolCall.toolName];
900
+ if (!clientTool || typeof clientTool.execute !== "function") continue;
901
+ let result;
902
+ let observability;
903
+ try {
904
+ const execution = await executeClientToolWithObservability({
905
+ clientTool,
906
+ args: toolCall.args,
907
+ toolName: toolCall.toolName,
908
+ parentContext: toolCall.observability,
909
+ executeContext: {
910
+ requestContext: activeRequestContext,
911
+ tracingContext: { currentSpan: void 0 },
912
+ agent: {
913
+ agentId: agent.agentId,
914
+ messages: finishPayload.payload?.messages?.nonUser ?? [],
915
+ toolCallId: toolCall.toolCallId,
916
+ suspend: async () => {
917
+ },
918
+ threadId,
919
+ resourceId
920
+ }
921
+ }
922
+ });
923
+ result = execution.result;
924
+ observability = execution.observability;
925
+ } catch (error) {
926
+ result = { error: String(error) };
927
+ }
928
+ const toolResultContent = {
929
+ type: "tool-result",
930
+ toolCallId: toolCall.toolCallId,
931
+ toolName: toolCall.toolName,
932
+ result
933
+ };
934
+ if (observability) {
935
+ toolResultContent.__mastraObservability = observability;
936
+ }
937
+ await onChunk({
938
+ type: "tool-result",
939
+ runId,
940
+ payload: toolResultContent
941
+ });
942
+ toolResultMessages.push({
943
+ role: "tool",
944
+ content: [toolResultContent]
945
+ });
946
+ }
947
+ if (toolResultMessages.length === 0) {
948
+ agent.deleteSignalRuntimeOptions(runId);
949
+ return;
950
+ }
951
+ try {
952
+ const continuation = await agent.streamUntilIdle(
953
+ [...finishPayload.payload?.messages?.nonUser ?? [], ...toolResultMessages],
954
+ {
955
+ ...activeRuntimeOptions,
956
+ runId: v4(),
957
+ requestContext: processedRequestContext,
958
+ memory: threadId ? { thread: threadId, resource: resourceId } : void 0,
959
+ clientTools: processedClientTools
960
+ }
961
+ );
962
+ try {
963
+ void continuation.body?.cancel?.();
964
+ } catch {
965
+ }
966
+ } catch (error) {
967
+ console.error("Error running client-tool continuation:", error);
968
+ } finally {
969
+ agent.deleteSignalRuntimeOptions(runId);
970
+ }
971
+ };
972
+ const reconnectOptions = reconnect === true ? { maxRetries: Infinity, delayMs: 1e3 } : reconnect ? { maxRetries: reconnect.maxRetries ?? Infinity, delayMs: reconnect.delayMs ?? 1e3 } : null;
973
+ let response = streamResponse;
974
+ let attempts = 0;
975
+ const onChunkErrorSentinel = /* @__PURE__ */ Symbol("onChunkErrorSentinel");
976
+ const guardedOnChunk = async (chunk) => {
977
+ try {
978
+ await onChunk(chunk);
979
+ await handleSubscribedChunk(chunk);
980
+ } catch (cause) {
981
+ throw { [onChunkErrorSentinel]: true, cause };
982
+ }
983
+ };
984
+ while (true) {
985
+ if (!response.body) {
986
+ throw new Error("No response body");
987
+ }
988
+ try {
989
+ await processMastraStream({
990
+ stream: response.body,
991
+ onChunk: guardedOnChunk,
992
+ signal: this.options.abortSignal
993
+ });
994
+ } catch (error) {
995
+ if (typeof error === "object" && error !== null && error[onChunkErrorSentinel]) {
996
+ throw error.cause;
997
+ }
998
+ if (!reconnectOptions || this.options.abortSignal?.aborted || attempts >= reconnectOptions.maxRetries) {
999
+ throw error;
1000
+ }
1001
+ }
1002
+ if (!reconnectOptions || this.options.abortSignal?.aborted || attempts >= reconnectOptions.maxRetries) {
1003
+ return;
1004
+ }
1005
+ while (attempts < reconnectOptions.maxRetries) {
1006
+ attempts++;
1007
+ if (this.options.abortSignal?.aborted) {
1008
+ return;
1009
+ }
1010
+ await new Promise((resolve) => setTimeout(resolve, reconnectOptions.delayMs));
1011
+ if (this.options.abortSignal?.aborted) {
1012
+ return;
1013
+ }
1014
+ try {
1015
+ response = await requestSubscription();
1016
+ break;
1017
+ } catch (error) {
1018
+ if (this.options.abortSignal?.aborted || attempts >= reconnectOptions.maxRetries) {
1019
+ throw error;
1020
+ }
1021
+ }
1022
+ }
1023
+ }
765
1024
  };
766
1025
  return streamResponse;
767
1026
  }