@upstash/workflow 0.2.11 → 0.2.13

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.
@@ -42,11 +42,12 @@ var WORKFLOW_PROTOCOL_VERSION_HEADER = "Upstash-Workflow-Sdk-Version";
42
42
  var DEFAULT_CONTENT_TYPE = "application/json";
43
43
  var NO_CONCURRENCY = 1;
44
44
  var DEFAULT_RETRIES = 3;
45
- var VERSION = "v0.2.7";
45
+ var VERSION = "v0.2.13";
46
46
  var SDK_TELEMETRY = `@upstash/workflow@${VERSION}`;
47
47
  var TELEMETRY_HEADER_SDK = "Upstash-Telemetry-Sdk";
48
48
  var TELEMETRY_HEADER_FRAMEWORK = "Upstash-Telemetry-Framework";
49
49
  var TELEMETRY_HEADER_RUNTIME = "Upstash-Telemetry-Runtime";
50
+ var TELEMETRY_HEADER_AGENT = "Upstash-Telemetry-Agent";
50
51
 
51
52
  // src/error.ts
52
53
  import { QstashError } from "@upstash/qstash";
@@ -85,7 +86,7 @@ var formatWorkflowError = (error) => {
85
86
  message: error.message
86
87
  } : {
87
88
  error: "Error",
88
- message: "An error occured while executing workflow."
89
+ message: `An error occured while executing workflow: '${typeof error === "string" ? error : JSON.stringify(error)}'`
89
90
  };
90
91
  };
91
92
 
@@ -101,6 +102,271 @@ var StepTypes = [
101
102
  "Invoke"
102
103
  ];
103
104
 
105
+ // src/agents/adapters.ts
106
+ import { tool } from "ai";
107
+
108
+ // src/agents/constants.ts
109
+ var AGENT_NAME_HEADER = "upstash-agent-name";
110
+ var MANAGER_AGENT_PROMPT = `You are an agent orchestrating other AI Agents.
111
+
112
+ These other agents have tools available to them.
113
+
114
+ Given a prompt, utilize these agents to address requests.
115
+
116
+ Don't always call all the agents provided to you at the same time. You can call one and use it's response to call another.
117
+
118
+ Avoid calling the same agent twice in one turn. Instead, prefer to call it once but provide everything
119
+ you need from that agent.
120
+ `;
121
+
122
+ // src/agents/adapters.ts
123
+ var fetchWithContextCall = async (context, agentCallParams, ...params) => {
124
+ const [input, init] = params;
125
+ try {
126
+ const headers = init?.headers ? Object.fromEntries(new Headers(init.headers).entries()) : {};
127
+ const body = init?.body ? JSON.parse(init.body) : void 0;
128
+ const agentName = headers[AGENT_NAME_HEADER];
129
+ const stepName = agentName ? `Call Agent ${agentName}` : "Call Agent";
130
+ const responseInfo = await context.call(stepName, {
131
+ url: input.toString(),
132
+ method: init?.method,
133
+ headers,
134
+ body,
135
+ timeout: agentCallParams?.timeout,
136
+ retries: agentCallParams?.retries,
137
+ flowControl: agentCallParams?.flowControl
138
+ });
139
+ const responseHeaders = new Headers(
140
+ Object.entries(responseInfo.header).reduce(
141
+ (acc, [key, values]) => {
142
+ acc[key] = values.join(", ");
143
+ return acc;
144
+ },
145
+ {}
146
+ )
147
+ );
148
+ return new Response(JSON.stringify(responseInfo.body), {
149
+ status: responseInfo.status,
150
+ headers: responseHeaders
151
+ });
152
+ } catch (error) {
153
+ if (error instanceof Error && error.name === "WorkflowAbort") {
154
+ throw error;
155
+ } else {
156
+ console.error("Error in fetch implementation:", error);
157
+ throw error;
158
+ }
159
+ }
160
+ };
161
+ var createWorkflowModel = ({
162
+ context,
163
+ provider,
164
+ providerParams,
165
+ agentCallParams
166
+ }) => {
167
+ return provider({
168
+ fetch: (...params) => fetchWithContextCall(context, agentCallParams, ...params),
169
+ ...providerParams
170
+ });
171
+ };
172
+ var wrapTools = ({
173
+ context,
174
+ tools
175
+ }) => {
176
+ return Object.fromEntries(
177
+ Object.entries(tools).map((toolInfo) => {
178
+ const [toolName, tool3] = toolInfo;
179
+ const executeAsStep = "executeAsStep" in tool3 ? tool3.executeAsStep : true;
180
+ const aiSDKTool = convertToAISDKTool(tool3);
181
+ const execute = aiSDKTool.execute;
182
+ if (execute && executeAsStep) {
183
+ const wrappedExecute = (...params) => {
184
+ return context.run(`Run tool ${toolName}`, () => execute(...params));
185
+ };
186
+ aiSDKTool.execute = wrappedExecute;
187
+ }
188
+ return [toolName, aiSDKTool];
189
+ })
190
+ );
191
+ };
192
+ var convertToAISDKTool = (tool3) => {
193
+ const isLangchainTool = "invoke" in tool3;
194
+ return isLangchainTool ? convertLangchainTool(tool3) : tool3;
195
+ };
196
+ var convertLangchainTool = (langchainTool) => {
197
+ return tool({
198
+ description: langchainTool.description,
199
+ parameters: langchainTool.schema,
200
+ execute: async (...param) => langchainTool.invoke(...param)
201
+ });
202
+ };
203
+ var WorkflowTool = class {
204
+ /**
205
+ * description of the tool
206
+ */
207
+ description;
208
+ /**
209
+ * schema of the tool
210
+ */
211
+ schema;
212
+ /**
213
+ * function to invoke the tool
214
+ */
215
+ invoke;
216
+ /**
217
+ * whether the invoke method of the tool is to be wrapped with `context.run`
218
+ */
219
+ executeAsStep;
220
+ /**
221
+ *
222
+ * @param description description of the tool
223
+ * @param schema schema of the tool
224
+ * @param invoke function to invoke the tool
225
+ * @param executeAsStep whether the invoke method of the tool is to be wrapped with `context.run`
226
+ */
227
+ constructor(params) {
228
+ this.description = params.description;
229
+ this.schema = params.schema;
230
+ this.invoke = params.invoke;
231
+ this.executeAsStep = params.executeAsStep ?? true;
232
+ }
233
+ };
234
+
235
+ // src/serve/serve-many.ts
236
+ var getWorkflowId = (url) => {
237
+ const components = url.split("/");
238
+ const lastComponent = components[components.length - 1];
239
+ return lastComponent.split("?")[0];
240
+ };
241
+ var serveManyBase = ({
242
+ workflows,
243
+ getUrl,
244
+ serveMethod,
245
+ options
246
+ }) => {
247
+ const workflowIds = [];
248
+ const workflowMap = Object.fromEntries(
249
+ Object.entries(workflows).map((workflow) => {
250
+ const workflowId = workflow[0];
251
+ if (workflowIds.includes(workflowId)) {
252
+ throw new WorkflowError(
253
+ `Duplicate workflow name found: '${workflowId}'. Please set different workflow names in serveMany.`
254
+ );
255
+ }
256
+ if (workflowId.includes("/")) {
257
+ throw new WorkflowError(
258
+ `Invalid workflow name found: '${workflowId}'. Workflow name cannot contain '/'.`
259
+ );
260
+ }
261
+ workflowIds.push(workflowId);
262
+ workflow[1].workflowId = workflowId;
263
+ workflow[1].options = {
264
+ ...options,
265
+ ...workflow[1].options
266
+ };
267
+ const params = [workflow[1].routeFunction, workflow[1].options];
268
+ const handler = serveMethod(...params);
269
+ return [workflowId, handler];
270
+ })
271
+ );
272
+ return {
273
+ handler: async (...params) => {
274
+ const url = getUrl(...params);
275
+ const pickedWorkflowId = getWorkflowId(url);
276
+ if (!pickedWorkflowId) {
277
+ return new Response(
278
+ `Unexpected request in serveMany. workflowId not set. Please update the URL of your request.`,
279
+ {
280
+ status: 404
281
+ }
282
+ );
283
+ }
284
+ const workflow = workflowMap[pickedWorkflowId];
285
+ if (!workflow) {
286
+ return new Response(
287
+ `No workflows in serveMany found for '${pickedWorkflowId}'. Please update the URL of your request.`,
288
+ {
289
+ status: 404
290
+ }
291
+ );
292
+ }
293
+ return await workflow(...params);
294
+ }
295
+ };
296
+ };
297
+ var getNewUrlFromWorkflowId = (url, workflowId) => {
298
+ if (!workflowId) {
299
+ throw new WorkflowError("You can only call workflow which has a workflowId");
300
+ }
301
+ return url.replace(/[^/]+$/, workflowId);
302
+ };
303
+
304
+ // src/context/auto-executor.ts
305
+ import { QstashError as QstashError5 } from "@upstash/qstash";
306
+
307
+ // src/qstash/headers.ts
308
+ import { QstashError as QstashError4 } from "@upstash/qstash";
309
+
310
+ // src/client/utils.ts
311
+ import { QstashError as QstashError2 } from "@upstash/qstash";
312
+ var makeNotifyRequest = async (requester, eventId, eventData) => {
313
+ const result = await requester.request({
314
+ path: ["v2", "notify", eventId],
315
+ method: "POST",
316
+ body: typeof eventData === "string" ? eventData : JSON.stringify(eventData)
317
+ });
318
+ return result;
319
+ };
320
+ var makeGetWaitersRequest = async (requester, eventId) => {
321
+ const result = await requester.request({
322
+ path: ["v2", "waiters", eventId],
323
+ method: "GET"
324
+ });
325
+ return result;
326
+ };
327
+ var makeCancelRequest = async (requester, workflowRunId) => {
328
+ await requester.request({
329
+ path: ["v2", "workflows", "runs", `${workflowRunId}?cancel=true`],
330
+ method: "DELETE",
331
+ parseResponseAsJson: false
332
+ });
333
+ return true;
334
+ };
335
+ var getSteps = async (requester, workflowRunId, messageId, debug) => {
336
+ try {
337
+ const steps = await requester.request({
338
+ path: ["v2", "workflows", "runs", workflowRunId],
339
+ parseResponseAsJson: true
340
+ });
341
+ if (!messageId) {
342
+ await debug?.log("INFO", "ENDPOINT_START", {
343
+ message: `Pulled ${steps.length} steps from QStashand returned them without filtering with messageId.`
344
+ });
345
+ return { steps, workflowRunEnded: false };
346
+ } else {
347
+ const index = steps.findIndex((item) => item.messageId === messageId);
348
+ if (index === -1) {
349
+ return { steps: [], workflowRunEnded: false };
350
+ }
351
+ const filteredSteps = steps.slice(0, index + 1);
352
+ await debug?.log("INFO", "ENDPOINT_START", {
353
+ message: `Pulled ${steps.length} steps from QStash and filtered them to ${filteredSteps.length} using messageId.`
354
+ });
355
+ return { steps: filteredSteps, workflowRunEnded: false };
356
+ }
357
+ } catch (error) {
358
+ if (error instanceof QstashError2 && error.status === 404) {
359
+ await debug?.log("WARN", "ENDPOINT_START", {
360
+ message: "Couldn't fetch workflow run steps. This can happen if the workflow run succesfully ends before some callback is executed.",
361
+ error
362
+ });
363
+ return { steps: void 0, workflowRunEnded: true };
364
+ } else {
365
+ throw error;
366
+ }
367
+ }
368
+ };
369
+
104
370
  // src/utils.ts
105
371
  var NANOID_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_";
106
372
  var NANOID_LENGTH = 21;
@@ -543,85 +809,27 @@ var fromThrowable = Result.fromThrowable;
543
809
 
544
810
  // src/workflow-requests.ts
545
811
  import { QstashError as QstashError3 } from "@upstash/qstash";
546
-
547
- // src/client/utils.ts
548
- import { QstashError as QstashError2 } from "@upstash/qstash";
549
- var makeNotifyRequest = async (requester, eventId, eventData) => {
550
- const result = await requester.request({
551
- path: ["v2", "notify", eventId],
552
- method: "POST",
553
- body: typeof eventData === "string" ? eventData : JSON.stringify(eventData)
554
- });
555
- return result;
556
- };
557
- var makeGetWaitersRequest = async (requester, eventId) => {
558
- const result = await requester.request({
559
- path: ["v2", "waiters", eventId],
560
- method: "GET"
561
- });
562
- return result;
563
- };
564
- var makeCancelRequest = async (requester, workflowRunId) => {
565
- await requester.request({
566
- path: ["v2", "workflows", "runs", `${workflowRunId}?cancel=true`],
567
- method: "DELETE",
568
- parseResponseAsJson: false
569
- });
570
- return true;
571
- };
572
- var getSteps = async (requester, workflowRunId, messageId, debug) => {
573
- try {
574
- const steps = await requester.request({
575
- path: ["v2", "workflows", "runs", workflowRunId],
576
- parseResponseAsJson: true
577
- });
578
- if (!messageId) {
579
- await debug?.log("INFO", "ENDPOINT_START", {
580
- message: `Pulled ${steps.length} steps from QStashand returned them without filtering with messageId.`
581
- });
582
- return { steps, workflowRunEnded: false };
583
- } else {
584
- const index = steps.findIndex((item) => item.messageId === messageId);
585
- if (index === -1) {
586
- return { steps: [], workflowRunEnded: false };
587
- }
588
- const filteredSteps = steps.slice(0, index + 1);
589
- await debug?.log("INFO", "ENDPOINT_START", {
590
- message: `Pulled ${steps.length} steps from QStash and filtered them to ${filteredSteps.length} using messageId.`
591
- });
592
- return { steps: filteredSteps, workflowRunEnded: false };
593
- }
594
- } catch (error) {
595
- if (error instanceof QstashError2 && error.status === 404) {
596
- await debug?.log("WARN", "ENDPOINT_START", {
597
- message: "Couldn't fetch workflow run steps. This can happen if the workflow run succesfully ends before some callback is executed.",
598
- error
599
- });
600
- return { steps: void 0, workflowRunEnded: true };
601
- } else {
602
- throw error;
603
- }
604
- }
605
- };
606
-
607
- // src/workflow-requests.ts
608
- var triggerFirstInvocation = async ({
609
- workflowContext,
610
- useJSONContent,
611
- telemetry,
612
- debug,
613
- invokeCount
614
- }) => {
615
- const { headers } = getHeaders({
616
- initHeaderValue: "true",
617
- workflowRunId: workflowContext.workflowRunId,
618
- workflowUrl: workflowContext.url,
619
- userHeaders: workflowContext.headers,
620
- failureUrl: workflowContext.failureUrl,
621
- retries: workflowContext.retries,
622
- telemetry,
623
- invokeCount,
624
- flowControl: workflowContext.flowControl
812
+ var triggerFirstInvocation = async ({
813
+ workflowContext,
814
+ useJSONContent,
815
+ telemetry,
816
+ debug,
817
+ invokeCount,
818
+ delay
819
+ }) => {
820
+ const { headers } = getHeaders({
821
+ initHeaderValue: "true",
822
+ workflowConfig: {
823
+ workflowRunId: workflowContext.workflowRunId,
824
+ workflowUrl: workflowContext.url,
825
+ failureUrl: workflowContext.failureUrl,
826
+ retries: workflowContext.retries,
827
+ telemetry,
828
+ flowControl: workflowContext.flowControl,
829
+ useJSONContent: useJSONContent ?? false
830
+ },
831
+ invokeCount: invokeCount ?? 0,
832
+ userHeaders: workflowContext.headers
625
833
  });
626
834
  if (workflowContext.headers.get("content-type")) {
627
835
  headers["content-type"] = workflowContext.headers.get("content-type");
@@ -635,7 +843,8 @@ var triggerFirstInvocation = async ({
635
843
  headers,
636
844
  method: "POST",
637
845
  body,
638
- url: workflowContext.url
846
+ url: workflowContext.url,
847
+ delay
639
848
  });
640
849
  if (result.deduplicated) {
641
850
  await debug?.log("WARN", "SUBMIT_FIRST_INVOCATION", {
@@ -791,14 +1000,16 @@ ${atob(callbackMessage.body ?? "")}`
791
1000
  const userHeaders = recreateUserHeaders(request.headers);
792
1001
  const { headers: requestHeaders } = getHeaders({
793
1002
  initHeaderValue: "false",
794
- workflowRunId,
795
- workflowUrl,
1003
+ workflowConfig: {
1004
+ workflowRunId,
1005
+ workflowUrl,
1006
+ failureUrl,
1007
+ retries,
1008
+ telemetry,
1009
+ flowControl
1010
+ },
796
1011
  userHeaders,
797
- failureUrl,
798
- retries,
799
- telemetry,
800
- invokeCount: Number(invokeCount),
801
- flowControl
1012
+ invokeCount: Number(invokeCount)
802
1013
  });
803
1014
  const callResponse = {
804
1015
  status: callbackMessage.status,
@@ -844,154 +1055,6 @@ var getTelemetryHeaders = (telemetry) => {
844
1055
  [TELEMETRY_HEADER_RUNTIME]: telemetry.runtime ?? "unknown"
845
1056
  };
846
1057
  };
847
- var getHeaders = ({
848
- initHeaderValue,
849
- workflowRunId,
850
- workflowUrl,
851
- userHeaders,
852
- failureUrl,
853
- retries,
854
- step,
855
- callRetries,
856
- callTimeout,
857
- telemetry,
858
- invokeCount,
859
- flowControl,
860
- callFlowControl
861
- }) => {
862
- const callHeaders = new Headers(step?.callHeaders);
863
- const contentType = (callHeaders.get("content-type") ? callHeaders.get("content-type") : userHeaders?.get("Content-Type") ? userHeaders.get("Content-Type") : void 0) ?? DEFAULT_CONTENT_TYPE;
864
- const baseHeaders = {
865
- [WORKFLOW_INIT_HEADER]: initHeaderValue,
866
- [WORKFLOW_ID_HEADER]: workflowRunId,
867
- [WORKFLOW_URL_HEADER]: workflowUrl,
868
- [WORKFLOW_FEATURE_HEADER]: "LazyFetch,InitialBody",
869
- [WORKFLOW_PROTOCOL_VERSION_HEADER]: WORKFLOW_PROTOCOL_VERSION,
870
- "content-type": contentType,
871
- ...telemetry ? getTelemetryHeaders(telemetry) : {}
872
- };
873
- if (invokeCount !== void 0 && !step?.callUrl) {
874
- baseHeaders[`Upstash-Forward-${WORKFLOW_INVOKE_COUNT_HEADER}`] = invokeCount.toString();
875
- }
876
- if (!step?.callUrl) {
877
- baseHeaders[`Upstash-Forward-${WORKFLOW_PROTOCOL_VERSION_HEADER}`] = WORKFLOW_PROTOCOL_VERSION;
878
- }
879
- if (callTimeout) {
880
- baseHeaders[`Upstash-Timeout`] = callTimeout.toString();
881
- }
882
- if (failureUrl) {
883
- baseHeaders[`Upstash-Failure-Callback-Forward-${WORKFLOW_FAILURE_HEADER}`] = "true";
884
- baseHeaders[`Upstash-Failure-Callback-Forward-Upstash-Workflow-Failure-Callback`] = "true";
885
- baseHeaders["Upstash-Failure-Callback-Workflow-Runid"] = workflowRunId;
886
- baseHeaders["Upstash-Failure-Callback-Workflow-Init"] = "false";
887
- baseHeaders["Upstash-Failure-Callback-Workflow-Url"] = workflowUrl;
888
- baseHeaders["Upstash-Failure-Callback-Workflow-Calltype"] = "failureCall";
889
- if (retries !== void 0) {
890
- baseHeaders["Upstash-Failure-Callback-Retries"] = retries.toString();
891
- }
892
- if (flowControl) {
893
- const { flowControlKey, flowControlValue } = prepareFlowControl(flowControl);
894
- baseHeaders["Upstash-Failure-Callback-Flow-Control-Key"] = flowControlKey;
895
- baseHeaders["Upstash-Failure-Callback-Flow-Control-Value"] = flowControlValue;
896
- }
897
- if (!step?.callUrl) {
898
- baseHeaders["Upstash-Failure-Callback"] = failureUrl;
899
- }
900
- }
901
- if (step?.callUrl) {
902
- baseHeaders["Upstash-Retries"] = callRetries?.toString() ?? "0";
903
- baseHeaders[WORKFLOW_FEATURE_HEADER] = "WF_NoDelete,InitialBody";
904
- if (retries !== void 0) {
905
- baseHeaders["Upstash-Callback-Retries"] = retries.toString();
906
- baseHeaders["Upstash-Failure-Callback-Retries"] = retries.toString();
907
- }
908
- if (callFlowControl) {
909
- const { flowControlKey, flowControlValue } = prepareFlowControl(callFlowControl);
910
- baseHeaders["Upstash-Flow-Control-Key"] = flowControlKey;
911
- baseHeaders["Upstash-Flow-Control-Value"] = flowControlValue;
912
- }
913
- if (flowControl) {
914
- const { flowControlKey, flowControlValue } = prepareFlowControl(flowControl);
915
- baseHeaders["Upstash-Callback-Flow-Control-Key"] = flowControlKey;
916
- baseHeaders["Upstash-Callback-Flow-Control-Value"] = flowControlValue;
917
- }
918
- } else {
919
- if (flowControl) {
920
- const { flowControlKey, flowControlValue } = prepareFlowControl(flowControl);
921
- baseHeaders["Upstash-Flow-Control-Key"] = flowControlKey;
922
- baseHeaders["Upstash-Flow-Control-Value"] = flowControlValue;
923
- }
924
- if (retries !== void 0) {
925
- baseHeaders["Upstash-Retries"] = retries.toString();
926
- baseHeaders["Upstash-Failure-Callback-Retries"] = retries.toString();
927
- }
928
- }
929
- if (userHeaders) {
930
- for (const header of userHeaders.keys()) {
931
- if (step?.callHeaders) {
932
- baseHeaders[`Upstash-Callback-Forward-${header}`] = userHeaders.get(header);
933
- } else {
934
- baseHeaders[`Upstash-Forward-${header}`] = userHeaders.get(header);
935
- }
936
- baseHeaders[`Upstash-Failure-Callback-Forward-${header}`] = userHeaders.get(header);
937
- }
938
- }
939
- if (step?.callHeaders) {
940
- const forwardedHeaders = Object.fromEntries(
941
- Object.entries(step.callHeaders).map(([header, value]) => [
942
- `Upstash-Forward-${header}`,
943
- value
944
- ])
945
- );
946
- return {
947
- headers: {
948
- ...baseHeaders,
949
- ...forwardedHeaders,
950
- "Upstash-Callback": workflowUrl,
951
- "Upstash-Callback-Workflow-RunId": workflowRunId,
952
- "Upstash-Callback-Workflow-CallType": "fromCallback",
953
- "Upstash-Callback-Workflow-Init": "false",
954
- "Upstash-Callback-Workflow-Url": workflowUrl,
955
- "Upstash-Callback-Feature-Set": "LazyFetch,InitialBody",
956
- "Upstash-Callback-Forward-Upstash-Workflow-Callback": "true",
957
- "Upstash-Callback-Forward-Upstash-Workflow-StepId": step.stepId.toString(),
958
- "Upstash-Callback-Forward-Upstash-Workflow-StepName": step.stepName,
959
- "Upstash-Callback-Forward-Upstash-Workflow-StepType": step.stepType,
960
- "Upstash-Callback-Forward-Upstash-Workflow-Concurrent": step.concurrent.toString(),
961
- "Upstash-Callback-Forward-Upstash-Workflow-ContentType": contentType,
962
- [`Upstash-Callback-Forward-${WORKFLOW_INVOKE_COUNT_HEADER}`]: (invokeCount ?? 0).toString(),
963
- "Upstash-Workflow-CallType": "toCallback"
964
- }
965
- };
966
- }
967
- if (step?.waitEventId) {
968
- return {
969
- headers: {
970
- ...baseHeaders,
971
- "Upstash-Workflow-CallType": "step"
972
- },
973
- timeoutHeaders: {
974
- // to include user headers:
975
- ...Object.fromEntries(
976
- Object.entries(baseHeaders).map(([header, value]) => [header, [value]])
977
- ),
978
- // to include telemetry headers:
979
- ...telemetry ? Object.fromEntries(
980
- Object.entries(getTelemetryHeaders(telemetry)).map(([header, value]) => [
981
- header,
982
- [value]
983
- ])
984
- ) : {},
985
- // note: using WORKFLOW_ID_HEADER doesn't work, because Runid -> RunId:
986
- "Upstash-Workflow-Runid": [workflowRunId],
987
- [WORKFLOW_INIT_HEADER]: ["false"],
988
- [WORKFLOW_URL_HEADER]: [workflowUrl],
989
- "Upstash-Workflow-CallType": ["step"]
990
- }
991
- };
992
- }
993
- return { headers: baseHeaders };
994
- };
995
1058
  var verifyRequest = async (body, signature, verifier) => {
996
1059
  if (!verifier) {
997
1060
  return;
@@ -1017,278 +1080,6 @@ If you want to disable QStash Verification, you should clear env variables QSTAS
1017
1080
  );
1018
1081
  }
1019
1082
  };
1020
- var prepareFlowControl = (flowControl) => {
1021
- const parallelism = flowControl.parallelism?.toString();
1022
- const rate = flowControl.ratePerSecond?.toString();
1023
- const controlValue = [
1024
- parallelism ? `parallelism=${parallelism}` : void 0,
1025
- rate ? `rate=${rate}` : void 0
1026
- ].filter(Boolean);
1027
- if (controlValue.length === 0) {
1028
- throw new QstashError3("Provide at least one of parallelism or ratePerSecond for flowControl");
1029
- }
1030
- return {
1031
- flowControlKey: flowControl.key,
1032
- flowControlValue: controlValue.join(", ")
1033
- };
1034
- };
1035
-
1036
- // src/serve/serve-many.ts
1037
- var getWorkflowId = (url) => {
1038
- const components = url.split("/");
1039
- const lastComponent = components[components.length - 1];
1040
- return lastComponent.split("?")[0];
1041
- };
1042
- var serveManyBase = ({
1043
- workflows,
1044
- getUrl,
1045
- serveMethod,
1046
- options
1047
- }) => {
1048
- const workflowIds = [];
1049
- const workflowMap = Object.fromEntries(
1050
- Object.entries(workflows).map((workflow) => {
1051
- const workflowId = workflow[0];
1052
- if (workflowIds.includes(workflowId)) {
1053
- throw new WorkflowError(
1054
- `Duplicate workflow name found: '${workflowId}'. Please set different workflow names in serveMany.`
1055
- );
1056
- }
1057
- if (workflowId.includes("/")) {
1058
- throw new WorkflowError(
1059
- `Invalid workflow name found: '${workflowId}'. Workflow name cannot contain '/'.`
1060
- );
1061
- }
1062
- workflowIds.push(workflowId);
1063
- workflow[1].workflowId = workflowId;
1064
- workflow[1].options = {
1065
- ...options,
1066
- ...workflow[1].options
1067
- };
1068
- const params = [workflow[1].routeFunction, workflow[1].options];
1069
- const handler = serveMethod(...params);
1070
- return [workflowId, handler];
1071
- })
1072
- );
1073
- return {
1074
- handler: async (...params) => {
1075
- const url = getUrl(...params);
1076
- const pickedWorkflowId = getWorkflowId(url);
1077
- if (!pickedWorkflowId) {
1078
- return new Response(
1079
- `Unexpected request in serveMany. workflowId not set. Please update the URL of your request.`,
1080
- {
1081
- status: 404
1082
- }
1083
- );
1084
- }
1085
- const workflow = workflowMap[pickedWorkflowId];
1086
- if (!workflow) {
1087
- return new Response(
1088
- `No workflows in serveMany found for '${pickedWorkflowId}'. Please update the URL of your request.`,
1089
- {
1090
- status: 404
1091
- }
1092
- );
1093
- }
1094
- return await workflow(...params);
1095
- }
1096
- };
1097
- };
1098
- var invokeWorkflow = async ({
1099
- settings,
1100
- invokeStep,
1101
- context,
1102
- invokeCount,
1103
- telemetry
1104
- }) => {
1105
- const {
1106
- body,
1107
- workflow,
1108
- headers = {},
1109
- workflowRunId = getWorkflowRunId(),
1110
- retries,
1111
- flowControl
1112
- } = settings;
1113
- const { workflowId } = workflow;
1114
- const {
1115
- retries: workflowRetries,
1116
- failureFunction,
1117
- failureUrl,
1118
- useJSONContent,
1119
- flowControl: workflowFlowControl
1120
- } = workflow.options;
1121
- if (!workflowId) {
1122
- throw new WorkflowError("You can only invoke workflow which has a workflowId");
1123
- }
1124
- const { headers: invokerHeaders } = getHeaders({
1125
- initHeaderValue: "false",
1126
- workflowRunId: context.workflowRunId,
1127
- workflowUrl: context.url,
1128
- userHeaders: context.headers,
1129
- failureUrl: context.failureUrl,
1130
- retries: context.retries,
1131
- telemetry,
1132
- invokeCount,
1133
- flowControl: context.flowControl
1134
- });
1135
- invokerHeaders["Upstash-Workflow-Runid"] = context.workflowRunId;
1136
- const newUrl = context.url.replace(/[^/]+$/, workflowId);
1137
- const { headers: triggerHeaders } = getHeaders({
1138
- initHeaderValue: "true",
1139
- workflowRunId,
1140
- workflowUrl: newUrl,
1141
- userHeaders: new Headers(headers),
1142
- retries: retries ?? workflowRetries,
1143
- telemetry,
1144
- failureUrl: failureFunction ? newUrl : failureUrl,
1145
- invokeCount: invokeCount + 1,
1146
- flowControl: flowControl ?? workflowFlowControl
1147
- });
1148
- triggerHeaders["Upstash-Workflow-Invoke"] = "true";
1149
- if (useJSONContent) {
1150
- triggerHeaders["content-type"] = "application/json";
1151
- }
1152
- const request = {
1153
- body: JSON.stringify(body),
1154
- headers: Object.fromEntries(
1155
- Object.entries(invokerHeaders).map((pairs) => [pairs[0], [pairs[1]]])
1156
- ),
1157
- workflowRunId: context.workflowRunId,
1158
- workflowUrl: context.url,
1159
- step: invokeStep
1160
- };
1161
- await context.qstashClient.publish({
1162
- headers: triggerHeaders,
1163
- method: "POST",
1164
- body: JSON.stringify(request),
1165
- url: newUrl
1166
- });
1167
- };
1168
-
1169
- // src/agents/adapters.ts
1170
- import { createOpenAI } from "@ai-sdk/openai";
1171
- import { tool } from "ai";
1172
-
1173
- // src/agents/constants.ts
1174
- var AGENT_NAME_HEADER = "upstash-agent-name";
1175
- var MANAGER_AGENT_PROMPT = `You are an agent orchestrating other AI Agents.
1176
-
1177
- These other agents have tools available to them.
1178
-
1179
- Given a prompt, utilize these agents to address requests.
1180
-
1181
- Don't always call all the agents provided to you at the same time. You can call one and use it's response to call another.
1182
-
1183
- Avoid calling the same agent twice in one turn. Instead, prefer to call it once but provide everything
1184
- you need from that agent.
1185
- `;
1186
-
1187
- // src/agents/adapters.ts
1188
- var createWorkflowOpenAI = (context, config) => {
1189
- const { baseURL, apiKey } = config ?? {};
1190
- return createOpenAI({
1191
- baseURL,
1192
- apiKey,
1193
- compatibility: "strict",
1194
- fetch: async (input, init) => {
1195
- try {
1196
- const headers = init?.headers ? Object.fromEntries(new Headers(init.headers).entries()) : {};
1197
- const body = init?.body ? JSON.parse(init.body) : void 0;
1198
- const agentName = headers[AGENT_NAME_HEADER];
1199
- const stepName = agentName ? `Call Agent ${agentName}` : "Call Agent";
1200
- const responseInfo = await context.call(stepName, {
1201
- url: input.toString(),
1202
- method: init?.method,
1203
- headers,
1204
- body
1205
- });
1206
- const responseHeaders = new Headers(
1207
- Object.entries(responseInfo.header).reduce(
1208
- (acc, [key, values]) => {
1209
- acc[key] = values.join(", ");
1210
- return acc;
1211
- },
1212
- {}
1213
- )
1214
- );
1215
- return new Response(JSON.stringify(responseInfo.body), {
1216
- status: responseInfo.status,
1217
- headers: responseHeaders
1218
- });
1219
- } catch (error) {
1220
- if (error instanceof Error && error.name === "WorkflowAbort") {
1221
- throw error;
1222
- } else {
1223
- console.error("Error in fetch implementation:", error);
1224
- throw error;
1225
- }
1226
- }
1227
- }
1228
- });
1229
- };
1230
- var wrapTools = ({
1231
- context,
1232
- tools
1233
- }) => {
1234
- return Object.fromEntries(
1235
- Object.entries(tools).map((toolInfo) => {
1236
- const [toolName, tool3] = toolInfo;
1237
- const executeAsStep = "executeAsStep" in tool3 ? tool3.executeAsStep : true;
1238
- const aiSDKTool = convertToAISDKTool(tool3);
1239
- const execute = aiSDKTool.execute;
1240
- if (execute && executeAsStep) {
1241
- const wrappedExecute = (...params) => {
1242
- return context.run(`Run tool ${toolName}`, () => execute(...params));
1243
- };
1244
- aiSDKTool.execute = wrappedExecute;
1245
- }
1246
- return [toolName, aiSDKTool];
1247
- })
1248
- );
1249
- };
1250
- var convertToAISDKTool = (tool3) => {
1251
- const isLangchainTool = "invoke" in tool3;
1252
- return isLangchainTool ? convertLangchainTool(tool3) : tool3;
1253
- };
1254
- var convertLangchainTool = (langchainTool) => {
1255
- return tool({
1256
- description: langchainTool.description,
1257
- parameters: langchainTool.schema,
1258
- execute: async (...param) => langchainTool.invoke(...param)
1259
- });
1260
- };
1261
- var WorkflowTool = class {
1262
- /**
1263
- * description of the tool
1264
- */
1265
- description;
1266
- /**
1267
- * schema of the tool
1268
- */
1269
- schema;
1270
- /**
1271
- * function to invoke the tool
1272
- */
1273
- invoke;
1274
- /**
1275
- * whether the invoke method of the tool is to be wrapped with `context.run`
1276
- */
1277
- executeAsStep;
1278
- /**
1279
- *
1280
- * @param description description of the tool
1281
- * @param schema schema of the tool
1282
- * @param invoke function to invoke the tool
1283
- * @param executeAsStep whether the invoke method of the tool is to be wrapped with `context.run`
1284
- */
1285
- constructor(params) {
1286
- this.description = params.description;
1287
- this.schema = params.schema;
1288
- this.invoke = params.invoke;
1289
- this.executeAsStep = params.executeAsStep ?? true;
1290
- }
1291
- };
1292
1083
 
1293
1084
  // src/context/steps.ts
1294
1085
  var BaseLazyStep = class _BaseLazyStep {
@@ -1299,6 +1090,11 @@ var BaseLazyStep = class _BaseLazyStep {
1299
1090
  "A workflow step name cannot be undefined or an empty string. Please provide a name for your workflow step."
1300
1091
  );
1301
1092
  }
1093
+ if (typeof stepName !== "string") {
1094
+ console.warn(
1095
+ "Workflow Warning: A workflow step name must be a string. In a future release, this will throw an error."
1096
+ );
1097
+ }
1302
1098
  this.stepName = stepName;
1303
1099
  }
1304
1100
  /**
@@ -1348,6 +1144,40 @@ var BaseLazyStep = class _BaseLazyStep {
1348
1144
  return stepOut;
1349
1145
  }
1350
1146
  }
1147
+ getBody({ step }) {
1148
+ step.out = JSON.stringify(step.out);
1149
+ return JSON.stringify(step);
1150
+ }
1151
+ getHeaders({ context, telemetry, invokeCount, step }) {
1152
+ return getHeaders({
1153
+ initHeaderValue: "false",
1154
+ workflowConfig: {
1155
+ workflowRunId: context.workflowRunId,
1156
+ workflowUrl: context.url,
1157
+ failureUrl: context.failureUrl,
1158
+ retries: context.retries,
1159
+ useJSONContent: false,
1160
+ telemetry,
1161
+ flowControl: context.flowControl
1162
+ },
1163
+ userHeaders: context.headers,
1164
+ invokeCount,
1165
+ stepInfo: {
1166
+ step,
1167
+ lazyStep: this
1168
+ }
1169
+ });
1170
+ }
1171
+ async submitStep({ context, body, headers }) {
1172
+ return await context.qstashClient.batch([
1173
+ {
1174
+ body,
1175
+ headers,
1176
+ method: "POST",
1177
+ url: context.url
1178
+ }
1179
+ ]);
1180
+ }
1351
1181
  };
1352
1182
  var LazyFunctionStep = class extends BaseLazyStep {
1353
1183
  stepFunction;
@@ -1407,6 +1237,17 @@ var LazySleepStep = class extends BaseLazyStep {
1407
1237
  concurrent
1408
1238
  });
1409
1239
  }
1240
+ async submitStep({ context, body, headers, isParallel }) {
1241
+ return await context.qstashClient.batch([
1242
+ {
1243
+ body,
1244
+ headers,
1245
+ method: "POST",
1246
+ url: context.url,
1247
+ delay: isParallel ? void 0 : this.sleep
1248
+ }
1249
+ ]);
1250
+ }
1410
1251
  };
1411
1252
  var LazySleepUntilStep = class extends BaseLazyStep {
1412
1253
  sleepUntil;
@@ -1438,6 +1279,17 @@ var LazySleepUntilStep = class extends BaseLazyStep {
1438
1279
  safeParseOut() {
1439
1280
  return void 0;
1440
1281
  }
1282
+ async submitStep({ context, body, headers, isParallel }) {
1283
+ return await context.qstashClient.batch([
1284
+ {
1285
+ body,
1286
+ headers,
1287
+ method: "POST",
1288
+ url: context.url,
1289
+ notBefore: isParallel ? void 0 : this.sleepUntil
1290
+ }
1291
+ ]);
1292
+ }
1441
1293
  };
1442
1294
  var LazyCallStep = class _LazyCallStep extends BaseLazyStep {
1443
1295
  url;
@@ -1521,6 +1373,58 @@ var LazyCallStep = class _LazyCallStep extends BaseLazyStep {
1521
1373
  }
1522
1374
  return false;
1523
1375
  };
1376
+ getBody({ step }) {
1377
+ if (!step.callUrl) {
1378
+ throw new WorkflowError("Incompatible step received in LazyCallStep.getBody");
1379
+ }
1380
+ return JSON.stringify(step.callBody);
1381
+ }
1382
+ getHeaders({ context, telemetry, invokeCount, step }) {
1383
+ const { headers, contentType } = super.getHeaders({ context, telemetry, invokeCount, step });
1384
+ headers["Upstash-Retries"] = this.retries.toString();
1385
+ headers[WORKFLOW_FEATURE_HEADER] = "WF_NoDelete,InitialBody";
1386
+ if (this.flowControl) {
1387
+ const { flowControlKey, flowControlValue } = prepareFlowControl(this.flowControl);
1388
+ headers["Upstash-Flow-Control-Key"] = flowControlKey;
1389
+ headers["Upstash-Flow-Control-Value"] = flowControlValue;
1390
+ }
1391
+ if (this.timeout) {
1392
+ headers["Upstash-Timeout"] = this.timeout.toString();
1393
+ }
1394
+ const forwardedHeaders = Object.fromEntries(
1395
+ Object.entries(this.headers).map(([header, value]) => [`Upstash-Forward-${header}`, value])
1396
+ );
1397
+ return {
1398
+ headers: {
1399
+ ...headers,
1400
+ ...forwardedHeaders,
1401
+ "Upstash-Callback": context.url,
1402
+ "Upstash-Callback-Workflow-RunId": context.workflowRunId,
1403
+ "Upstash-Callback-Workflow-CallType": "fromCallback",
1404
+ "Upstash-Callback-Workflow-Init": "false",
1405
+ "Upstash-Callback-Workflow-Url": context.url,
1406
+ "Upstash-Callback-Feature-Set": "LazyFetch,InitialBody",
1407
+ "Upstash-Callback-Forward-Upstash-Workflow-Callback": "true",
1408
+ "Upstash-Callback-Forward-Upstash-Workflow-StepId": step.stepId.toString(),
1409
+ "Upstash-Callback-Forward-Upstash-Workflow-StepName": this.stepName,
1410
+ "Upstash-Callback-Forward-Upstash-Workflow-StepType": this.stepType,
1411
+ "Upstash-Callback-Forward-Upstash-Workflow-Concurrent": step.concurrent.toString(),
1412
+ "Upstash-Callback-Forward-Upstash-Workflow-ContentType": contentType,
1413
+ "Upstash-Workflow-CallType": "toCallback"
1414
+ },
1415
+ contentType
1416
+ };
1417
+ }
1418
+ async submitStep({ context, headers }) {
1419
+ return await context.qstashClient.batch([
1420
+ {
1421
+ headers,
1422
+ body: JSON.stringify(this.body),
1423
+ method: this.method,
1424
+ url: this.url
1425
+ }
1426
+ ]);
1427
+ }
1524
1428
  };
1525
1429
  var LazyWaitForEventStep = class extends BaseLazyStep {
1526
1430
  eventId;
@@ -1560,6 +1464,57 @@ var LazyWaitForEventStep = class extends BaseLazyStep {
1560
1464
  eventData: BaseLazyStep.tryParsing(result.eventData)
1561
1465
  };
1562
1466
  }
1467
+ getHeaders({ context, telemetry, invokeCount, step }) {
1468
+ const headers = super.getHeaders({ context, telemetry, invokeCount, step });
1469
+ headers.headers["Upstash-Workflow-CallType"] = "step";
1470
+ return headers;
1471
+ }
1472
+ getBody({ context, step, headers, telemetry }) {
1473
+ if (!step.waitEventId) {
1474
+ throw new WorkflowError("Incompatible step received in LazyWaitForEventStep.getBody");
1475
+ }
1476
+ const timeoutHeaders = {
1477
+ // to include user headers:
1478
+ ...Object.fromEntries(Object.entries(headers).map(([header, value]) => [header, [value]])),
1479
+ // to include telemetry headers:
1480
+ ...telemetry ? Object.fromEntries(
1481
+ Object.entries(getTelemetryHeaders(telemetry)).map(([header, value]) => [
1482
+ header,
1483
+ [value]
1484
+ ])
1485
+ ) : {},
1486
+ // note: using WORKFLOW_ID_HEADER doesn't work, because Runid -> RunId:
1487
+ "Upstash-Workflow-Runid": [context.workflowRunId],
1488
+ [WORKFLOW_INIT_HEADER]: ["false"],
1489
+ [WORKFLOW_URL_HEADER]: [context.url],
1490
+ "Upstash-Workflow-CallType": ["step"]
1491
+ };
1492
+ const waitBody = {
1493
+ url: context.url,
1494
+ timeout: step.timeout,
1495
+ timeoutBody: void 0,
1496
+ timeoutUrl: context.url,
1497
+ timeoutHeaders,
1498
+ step: {
1499
+ stepId: step.stepId,
1500
+ stepType: "Wait",
1501
+ stepName: step.stepName,
1502
+ concurrent: step.concurrent,
1503
+ targetStep: step.targetStep
1504
+ }
1505
+ };
1506
+ return JSON.stringify(waitBody);
1507
+ }
1508
+ async submitStep({ context, body, headers }) {
1509
+ const result = await context.qstashClient.http.request({
1510
+ path: ["v2", "wait", this.eventId],
1511
+ body,
1512
+ headers,
1513
+ method: "POST",
1514
+ parseResponseAsJson: false
1515
+ });
1516
+ return [result];
1517
+ }
1563
1518
  };
1564
1519
  var LazyNotifyStep = class extends LazyFunctionStep {
1565
1520
  stepType = "Notify";
@@ -1585,6 +1540,10 @@ var LazyInvokeStep = class extends BaseLazyStep {
1585
1540
  stepType = "Invoke";
1586
1541
  params;
1587
1542
  allowUndefinedOut = false;
1543
+ /**
1544
+ * workflow id of the invoked workflow
1545
+ */
1546
+ workflowId;
1588
1547
  constructor(stepName, {
1589
1548
  workflow,
1590
1549
  body,
@@ -1602,6 +1561,11 @@ var LazyInvokeStep = class extends BaseLazyStep {
1602
1561
  retries,
1603
1562
  flowControl
1604
1563
  };
1564
+ const { workflowId } = workflow;
1565
+ if (!workflowId) {
1566
+ throw new WorkflowError("You can only invoke workflow which has a workflowId");
1567
+ }
1568
+ this.workflowId = workflowId;
1605
1569
  }
1606
1570
  getPlanStep(concurrent, targetStep) {
1607
1571
  return {
@@ -1631,10 +1595,348 @@ var LazyInvokeStep = class extends BaseLazyStep {
1631
1595
  body: BaseLazyStep.tryParsing(result.body)
1632
1596
  };
1633
1597
  }
1598
+ getBody({ context, step, telemetry, invokeCount }) {
1599
+ const { headers: invokerHeaders } = getHeaders({
1600
+ initHeaderValue: "false",
1601
+ workflowConfig: {
1602
+ workflowRunId: context.workflowRunId,
1603
+ workflowUrl: context.url,
1604
+ failureUrl: context.failureUrl,
1605
+ retries: context.retries,
1606
+ telemetry,
1607
+ flowControl: context.flowControl,
1608
+ useJSONContent: false
1609
+ },
1610
+ userHeaders: context.headers,
1611
+ invokeCount
1612
+ });
1613
+ invokerHeaders["Upstash-Workflow-Runid"] = context.workflowRunId;
1614
+ const request = {
1615
+ body: JSON.stringify(this.params.body),
1616
+ headers: Object.fromEntries(
1617
+ Object.entries(invokerHeaders).map((pairs) => [pairs[0], [pairs[1]]])
1618
+ ),
1619
+ workflowRunId: context.workflowRunId,
1620
+ workflowUrl: context.url,
1621
+ step
1622
+ };
1623
+ return JSON.stringify(request);
1624
+ }
1625
+ getHeaders({ context, telemetry, invokeCount }) {
1626
+ const {
1627
+ workflow,
1628
+ headers = {},
1629
+ workflowRunId = getWorkflowRunId(),
1630
+ retries,
1631
+ flowControl
1632
+ } = this.params;
1633
+ const newUrl = context.url.replace(/[^/]+$/, this.workflowId);
1634
+ const {
1635
+ retries: workflowRetries,
1636
+ failureFunction,
1637
+ failureUrl,
1638
+ useJSONContent,
1639
+ flowControl: workflowFlowControl
1640
+ } = workflow.options;
1641
+ const { headers: triggerHeaders, contentType } = getHeaders({
1642
+ initHeaderValue: "true",
1643
+ workflowConfig: {
1644
+ workflowRunId,
1645
+ workflowUrl: newUrl,
1646
+ retries: retries ?? workflowRetries,
1647
+ telemetry,
1648
+ failureUrl: failureFunction ? newUrl : failureUrl,
1649
+ flowControl: flowControl ?? workflowFlowControl,
1650
+ useJSONContent: useJSONContent ?? false
1651
+ },
1652
+ invokeCount: invokeCount + 1,
1653
+ userHeaders: new Headers(headers)
1654
+ });
1655
+ triggerHeaders["Upstash-Workflow-Invoke"] = "true";
1656
+ return { headers: triggerHeaders, contentType };
1657
+ }
1658
+ async submitStep({ context, body, headers }) {
1659
+ const newUrl = context.url.replace(/[^/]+$/, this.workflowId);
1660
+ const result = await context.qstashClient.publish({
1661
+ headers,
1662
+ method: "POST",
1663
+ body,
1664
+ url: newUrl
1665
+ });
1666
+ return [result];
1667
+ }
1668
+ };
1669
+
1670
+ // src/qstash/headers.ts
1671
+ var WorkflowHeaders = class {
1672
+ userHeaders;
1673
+ workflowConfig;
1674
+ invokeCount;
1675
+ initHeaderValue;
1676
+ stepInfo;
1677
+ headers;
1678
+ constructor({
1679
+ userHeaders,
1680
+ workflowConfig,
1681
+ invokeCount,
1682
+ initHeaderValue,
1683
+ stepInfo
1684
+ }) {
1685
+ this.userHeaders = userHeaders;
1686
+ this.workflowConfig = workflowConfig;
1687
+ this.invokeCount = invokeCount;
1688
+ this.initHeaderValue = initHeaderValue;
1689
+ this.stepInfo = stepInfo;
1690
+ this.headers = {
1691
+ rawHeaders: {},
1692
+ workflowHeaders: {},
1693
+ failureHeaders: {}
1694
+ };
1695
+ }
1696
+ getHeaders() {
1697
+ this.addBaseHeaders();
1698
+ this.addRetries();
1699
+ this.addFlowControl();
1700
+ this.addUserHeaders();
1701
+ this.addInvokeCount();
1702
+ this.addFailureUrl();
1703
+ const contentType = this.addContentType();
1704
+ return this.prefixHeaders(contentType);
1705
+ }
1706
+ addBaseHeaders() {
1707
+ this.headers.rawHeaders = {
1708
+ ...this.headers.rawHeaders,
1709
+ [WORKFLOW_INIT_HEADER]: this.initHeaderValue,
1710
+ [WORKFLOW_ID_HEADER]: this.workflowConfig.workflowRunId,
1711
+ [WORKFLOW_URL_HEADER]: this.workflowConfig.workflowUrl,
1712
+ [WORKFLOW_FEATURE_HEADER]: "LazyFetch,InitialBody",
1713
+ [WORKFLOW_PROTOCOL_VERSION_HEADER]: WORKFLOW_PROTOCOL_VERSION,
1714
+ ...this.workflowConfig.telemetry ? getTelemetryHeaders(this.workflowConfig.telemetry) : {},
1715
+ ...this.workflowConfig.telemetry && this.stepInfo?.lazyStep instanceof LazyCallStep && this.stepInfo.lazyStep.headers[AGENT_NAME_HEADER] ? { [TELEMETRY_HEADER_AGENT]: "true" } : {}
1716
+ };
1717
+ if (this.stepInfo?.lazyStep.stepType !== "Call") {
1718
+ this.headers.rawHeaders[`Upstash-Forward-${WORKFLOW_PROTOCOL_VERSION_HEADER}`] = WORKFLOW_PROTOCOL_VERSION;
1719
+ }
1720
+ }
1721
+ addInvokeCount() {
1722
+ if (this.invokeCount === void 0 || this.invokeCount === 0) {
1723
+ return;
1724
+ }
1725
+ const invokeCount = this.invokeCount.toString();
1726
+ this.headers.workflowHeaders[`Forward-${WORKFLOW_INVOKE_COUNT_HEADER}`] = invokeCount;
1727
+ if (this.workflowConfig.failureUrl) {
1728
+ this.headers.failureHeaders[`Forward-${WORKFLOW_INVOKE_COUNT_HEADER}`] = invokeCount;
1729
+ }
1730
+ if (this.stepInfo?.lazyStep instanceof LazyCallStep) {
1731
+ this.headers.rawHeaders[`Upstash-Forward-${WORKFLOW_INVOKE_COUNT_HEADER}`] = invokeCount;
1732
+ }
1733
+ }
1734
+ addRetries() {
1735
+ if (this.workflowConfig.retries === void 0 || this.workflowConfig.retries === DEFAULT_RETRIES) {
1736
+ return;
1737
+ }
1738
+ const retries = this.workflowConfig.retries.toString();
1739
+ this.headers.workflowHeaders["Retries"] = retries;
1740
+ if (this.workflowConfig.failureUrl) {
1741
+ this.headers.failureHeaders["Retries"] = retries;
1742
+ }
1743
+ }
1744
+ addFlowControl() {
1745
+ if (!this.workflowConfig.flowControl) {
1746
+ return;
1747
+ }
1748
+ const { flowControlKey, flowControlValue } = prepareFlowControl(
1749
+ this.workflowConfig.flowControl
1750
+ );
1751
+ this.headers.workflowHeaders["Flow-Control-Key"] = flowControlKey;
1752
+ this.headers.workflowHeaders["Flow-Control-Value"] = flowControlValue;
1753
+ if (this.workflowConfig.failureUrl) {
1754
+ this.headers.failureHeaders["Flow-Control-Key"] = flowControlKey;
1755
+ this.headers.failureHeaders["Flow-Control-Value"] = flowControlValue;
1756
+ }
1757
+ }
1758
+ addUserHeaders() {
1759
+ for (const [key, value] of this.userHeaders.entries()) {
1760
+ const forwardKey = `Forward-${key}`;
1761
+ this.headers.workflowHeaders[forwardKey] = value;
1762
+ if (this.workflowConfig.failureUrl) {
1763
+ this.headers.failureHeaders[forwardKey] = value;
1764
+ }
1765
+ }
1766
+ }
1767
+ addFailureUrl() {
1768
+ if (!this.workflowConfig.failureUrl) {
1769
+ return;
1770
+ }
1771
+ this.headers.workflowHeaders["Failure-Callback"] = this.workflowConfig.failureUrl;
1772
+ this.headers.failureHeaders[`Forward-${WORKFLOW_FAILURE_HEADER}`] = "true";
1773
+ this.headers.failureHeaders[`Forward-Upstash-Workflow-Failure-Callback`] = "true";
1774
+ this.headers.failureHeaders["Workflow-Runid"] = this.workflowConfig.workflowRunId;
1775
+ this.headers.failureHeaders["Workflow-Init"] = "false";
1776
+ this.headers.failureHeaders["Workflow-Url"] = this.workflowConfig.workflowUrl;
1777
+ this.headers.failureHeaders["Workflow-Calltype"] = "failureCall";
1778
+ this.headers.failureHeaders["Feature-Set"] = "LazyFetch,InitialBody";
1779
+ if (this.workflowConfig.retries !== void 0 && this.workflowConfig.retries !== DEFAULT_RETRIES) {
1780
+ this.headers.failureHeaders["Retries"] = this.workflowConfig.retries.toString();
1781
+ }
1782
+ }
1783
+ addContentType() {
1784
+ if (this.workflowConfig.useJSONContent) {
1785
+ this.headers.rawHeaders["content-type"] = "application/json";
1786
+ return "application/json";
1787
+ }
1788
+ const callHeaders = new Headers(
1789
+ this.stepInfo?.lazyStep instanceof LazyCallStep ? this.stepInfo.lazyStep.headers : {}
1790
+ );
1791
+ const contentType = (callHeaders.get("content-type") ? callHeaders.get("content-type") : this.userHeaders?.get("Content-Type") ? this.userHeaders.get("Content-Type") : void 0) ?? DEFAULT_CONTENT_TYPE;
1792
+ this.headers.rawHeaders["content-type"] = contentType;
1793
+ return contentType;
1794
+ }
1795
+ prefixHeaders(contentType) {
1796
+ const { rawHeaders, workflowHeaders, failureHeaders } = this.headers;
1797
+ const isCall = this.stepInfo?.lazyStep.stepType === "Call";
1798
+ return {
1799
+ headers: {
1800
+ ...rawHeaders,
1801
+ ...addPrefixToHeaders(workflowHeaders, isCall ? "Upstash-Callback-" : "Upstash-"),
1802
+ ...addPrefixToHeaders(failureHeaders, "Upstash-Failure-Callback-"),
1803
+ ...isCall ? addPrefixToHeaders(failureHeaders, "Upstash-Callback-Failure-Callback-") : {}
1804
+ },
1805
+ contentType
1806
+ };
1807
+ }
1808
+ };
1809
+ function addPrefixToHeaders(headers, prefix) {
1810
+ const prefixedHeaders = {};
1811
+ for (const [key, value] of Object.entries(headers)) {
1812
+ prefixedHeaders[`${prefix}${key}`] = value;
1813
+ }
1814
+ return prefixedHeaders;
1815
+ }
1816
+ var prepareFlowControl = (flowControl) => {
1817
+ const parallelism = flowControl.parallelism?.toString();
1818
+ const rate = (flowControl.rate ?? flowControl.ratePerSecond)?.toString();
1819
+ const period = typeof flowControl.period === "number" ? `${flowControl.period}s` : flowControl.period;
1820
+ const controlValue = [
1821
+ parallelism ? `parallelism=${parallelism}` : void 0,
1822
+ rate ? `rate=${rate}` : void 0,
1823
+ period ? `period=${period}` : void 0
1824
+ ].filter(Boolean);
1825
+ if (controlValue.length === 0) {
1826
+ throw new QstashError4("Provide at least one of parallelism or ratePerSecond for flowControl");
1827
+ }
1828
+ return {
1829
+ flowControlKey: flowControl.key,
1830
+ flowControlValue: controlValue.join(", ")
1831
+ };
1832
+ };
1833
+ var getHeaders = (params) => {
1834
+ const workflowHeaders = new WorkflowHeaders(params);
1835
+ return workflowHeaders.getHeaders();
1836
+ };
1837
+
1838
+ // src/qstash/submit-steps.ts
1839
+ var submitParallelSteps = async ({
1840
+ context,
1841
+ steps,
1842
+ initialStepCount,
1843
+ invokeCount,
1844
+ telemetry,
1845
+ debug
1846
+ }) => {
1847
+ const planSteps = steps.map(
1848
+ (step, index) => step.getPlanStep(steps.length, initialStepCount + index)
1849
+ );
1850
+ await debug?.log("SUBMIT", "SUBMIT_STEP", {
1851
+ length: planSteps.length,
1852
+ steps: planSteps
1853
+ });
1854
+ const result = await context.qstashClient.batch(
1855
+ planSteps.map((planStep) => {
1856
+ const { headers } = getHeaders({
1857
+ initHeaderValue: "false",
1858
+ workflowConfig: {
1859
+ workflowRunId: context.workflowRunId,
1860
+ workflowUrl: context.url,
1861
+ failureUrl: context.failureUrl,
1862
+ retries: context.retries,
1863
+ flowControl: context.flowControl,
1864
+ telemetry
1865
+ },
1866
+ userHeaders: context.headers,
1867
+ invokeCount
1868
+ });
1869
+ return {
1870
+ headers,
1871
+ method: "POST",
1872
+ url: context.url,
1873
+ body: JSON.stringify(planStep),
1874
+ notBefore: planStep.sleepUntil,
1875
+ delay: planStep.sleepFor
1876
+ };
1877
+ })
1878
+ );
1879
+ await debug?.log("INFO", "SUBMIT_STEP", {
1880
+ messageIds: result.map((message) => {
1881
+ return {
1882
+ message: message.messageId
1883
+ };
1884
+ })
1885
+ });
1886
+ throw new WorkflowAbort(planSteps[0].stepName, planSteps[0]);
1887
+ };
1888
+ var submitSingleStep = async ({
1889
+ context,
1890
+ lazyStep,
1891
+ stepId,
1892
+ invokeCount,
1893
+ concurrency,
1894
+ telemetry,
1895
+ debug
1896
+ }) => {
1897
+ const resultStep = await lazyStep.getResultStep(concurrency, stepId);
1898
+ await debug?.log("INFO", "RUN_SINGLE", {
1899
+ fromRequest: false,
1900
+ step: resultStep,
1901
+ stepCount: stepId
1902
+ });
1903
+ const { headers } = lazyStep.getHeaders({
1904
+ context,
1905
+ step: resultStep,
1906
+ invokeCount,
1907
+ telemetry
1908
+ });
1909
+ const body = lazyStep.getBody({
1910
+ context,
1911
+ step: resultStep,
1912
+ headers,
1913
+ invokeCount,
1914
+ telemetry
1915
+ });
1916
+ await debug?.log("SUBMIT", "SUBMIT_STEP", {
1917
+ length: 1,
1918
+ steps: [resultStep]
1919
+ });
1920
+ const submitResult = await lazyStep.submitStep({
1921
+ context,
1922
+ body,
1923
+ headers,
1924
+ isParallel: concurrency !== NO_CONCURRENCY,
1925
+ invokeCount,
1926
+ step: resultStep,
1927
+ telemetry
1928
+ });
1929
+ await debug?.log("INFO", "SUBMIT_STEP", {
1930
+ messageIds: submitResult.map((message) => {
1931
+ return {
1932
+ message: message.messageId
1933
+ };
1934
+ })
1935
+ });
1936
+ return resultStep;
1634
1937
  };
1635
1938
 
1636
1939
  // src/context/auto-executor.ts
1637
- import { QstashError as QstashError4 } from "@upstash/qstash";
1638
1940
  var AutoExecutor = class _AutoExecutor {
1639
1941
  context;
1640
1942
  promises = /* @__PURE__ */ new WeakMap();
@@ -1738,14 +2040,16 @@ var AutoExecutor = class _AutoExecutor {
1738
2040
  });
1739
2041
  return lazyStep.parseOut(step.out);
1740
2042
  }
1741
- const resultStep = await lazyStep.getResultStep(NO_CONCURRENCY, this.stepCount);
1742
- await this.debug?.log("INFO", "RUN_SINGLE", {
1743
- fromRequest: false,
1744
- step: resultStep,
1745
- stepCount: this.stepCount
2043
+ const resultStep = await submitSingleStep({
2044
+ context: this.context,
2045
+ lazyStep,
2046
+ stepId: this.stepCount,
2047
+ invokeCount: this.invokeCount,
2048
+ concurrency: 1,
2049
+ telemetry: this.telemetry,
2050
+ debug: this.debug
1746
2051
  });
1747
- await this.submitStepsToQStash([resultStep], [lazyStep]);
1748
- return resultStep.out;
2052
+ throw new WorkflowAbort(lazyStep.stepName, resultStep);
1749
2053
  }
1750
2054
  /**
1751
2055
  * Runs steps in parallel.
@@ -1773,10 +2077,14 @@ var AutoExecutor = class _AutoExecutor {
1773
2077
  });
1774
2078
  switch (parallelCallState) {
1775
2079
  case "first": {
1776
- const planSteps = parallelSteps.map(
1777
- (parallelStep, index) => parallelStep.getPlanStep(parallelSteps.length, initialStepCount + index)
1778
- );
1779
- await this.submitStepsToQStash(planSteps, parallelSteps);
2080
+ await submitParallelSteps({
2081
+ context: this.context,
2082
+ steps: parallelSteps,
2083
+ initialStepCount,
2084
+ invokeCount: this.invokeCount,
2085
+ telemetry: this.telemetry,
2086
+ debug: this.debug
2087
+ });
1780
2088
  break;
1781
2089
  }
1782
2090
  case "partial": {
@@ -1790,13 +2098,18 @@ var AutoExecutor = class _AutoExecutor {
1790
2098
  validateStep(parallelSteps[stepIndex], planStep);
1791
2099
  try {
1792
2100
  const parallelStep = parallelSteps[stepIndex];
1793
- const resultStep = await parallelStep.getResultStep(
1794
- parallelSteps.length,
1795
- planStep.targetStep
1796
- );
1797
- await this.submitStepsToQStash([resultStep], [parallelStep]);
2101
+ const resultStep = await submitSingleStep({
2102
+ context: this.context,
2103
+ lazyStep: parallelStep,
2104
+ stepId: planStep.targetStep,
2105
+ invokeCount: this.invokeCount,
2106
+ concurrency: parallelSteps.length,
2107
+ telemetry: this.telemetry,
2108
+ debug: this.debug
2109
+ });
2110
+ throw new WorkflowAbort(parallelStep.stepName, resultStep);
1798
2111
  } catch (error) {
1799
- if (error instanceof WorkflowAbort || error instanceof QstashError4 && error.status === 400) {
2112
+ if (error instanceof WorkflowAbort || error instanceof QstashError5 && error.status === 400) {
1800
2113
  throw error;
1801
2114
  }
1802
2115
  throw new WorkflowError(
@@ -1852,128 +2165,6 @@ var AutoExecutor = class _AutoExecutor {
1852
2165
  return "discard";
1853
2166
  }
1854
2167
  }
1855
- /**
1856
- * sends the steps to QStash as batch
1857
- *
1858
- * @param steps steps to send
1859
- */
1860
- async submitStepsToQStash(steps, lazySteps) {
1861
- if (steps.length === 0) {
1862
- throw new WorkflowError(
1863
- `Unable to submit steps to QStash. Provided list is empty. Current step: ${this.stepCount}`
1864
- );
1865
- }
1866
- await this.debug?.log("SUBMIT", "SUBMIT_STEP", {
1867
- length: steps.length,
1868
- steps
1869
- });
1870
- if (steps[0].waitEventId && steps.length === 1) {
1871
- const waitStep = steps[0];
1872
- const { headers, timeoutHeaders } = getHeaders({
1873
- initHeaderValue: "false",
1874
- workflowRunId: this.context.workflowRunId,
1875
- workflowUrl: this.context.url,
1876
- userHeaders: this.context.headers,
1877
- step: waitStep,
1878
- failureUrl: this.context.failureUrl,
1879
- retries: this.context.retries,
1880
- telemetry: this.telemetry,
1881
- invokeCount: this.invokeCount,
1882
- flowControl: this.context.flowControl
1883
- });
1884
- const waitBody = {
1885
- url: this.context.url,
1886
- timeout: waitStep.timeout,
1887
- timeoutBody: void 0,
1888
- timeoutUrl: this.context.url,
1889
- timeoutHeaders,
1890
- step: {
1891
- stepId: waitStep.stepId,
1892
- stepType: "Wait",
1893
- stepName: waitStep.stepName,
1894
- concurrent: waitStep.concurrent,
1895
- targetStep: waitStep.targetStep
1896
- }
1897
- };
1898
- await this.context.qstashClient.http.request({
1899
- path: ["v2", "wait", waitStep.waitEventId],
1900
- body: JSON.stringify(waitBody),
1901
- headers,
1902
- method: "POST",
1903
- parseResponseAsJson: false
1904
- });
1905
- throw new WorkflowAbort(waitStep.stepName, waitStep);
1906
- }
1907
- if (steps.length === 1 && lazySteps[0] instanceof LazyInvokeStep) {
1908
- const invokeStep = steps[0];
1909
- const lazyInvokeStep = lazySteps[0];
1910
- await invokeWorkflow({
1911
- settings: lazyInvokeStep.params,
1912
- invokeStep,
1913
- context: this.context,
1914
- invokeCount: this.invokeCount,
1915
- telemetry: this.telemetry
1916
- });
1917
- throw new WorkflowAbort(invokeStep.stepName, invokeStep);
1918
- }
1919
- const result = await this.context.qstashClient.batch(
1920
- steps.map((singleStep, index) => {
1921
- const lazyStep = lazySteps[index];
1922
- const { headers } = getHeaders({
1923
- initHeaderValue: "false",
1924
- workflowRunId: this.context.workflowRunId,
1925
- workflowUrl: this.context.url,
1926
- userHeaders: this.context.headers,
1927
- step: singleStep,
1928
- failureUrl: this.context.failureUrl,
1929
- retries: this.context.retries,
1930
- callRetries: lazyStep instanceof LazyCallStep ? lazyStep.retries : void 0,
1931
- callTimeout: lazyStep instanceof LazyCallStep ? lazyStep.timeout : void 0,
1932
- telemetry: this.telemetry,
1933
- invokeCount: this.invokeCount,
1934
- flowControl: this.context.flowControl,
1935
- callFlowControl: lazyStep instanceof LazyCallStep ? lazyStep.flowControl : void 0
1936
- });
1937
- const willWait = singleStep.concurrent === NO_CONCURRENCY || singleStep.stepId === 0;
1938
- singleStep.out = JSON.stringify(singleStep.out);
1939
- return singleStep.callUrl && lazyStep instanceof LazyCallStep ? (
1940
- // if the step is a third party call, we call the third party
1941
- // url (singleStep.callUrl) and pass information about the workflow
1942
- // in the headers (handled in getHeaders). QStash makes the request
1943
- // to callUrl and returns the result to Workflow endpoint.
1944
- // handleThirdPartyCallResult method sends the result of the third
1945
- // party call to QStash.
1946
- {
1947
- headers,
1948
- method: singleStep.callMethod,
1949
- body: JSON.stringify(singleStep.callBody),
1950
- url: singleStep.callUrl
1951
- }
1952
- ) : (
1953
- // if the step is not a third party call, we use workflow
1954
- // endpoint (context.url) as URL when calling QStash. QStash
1955
- // calls us back with the updated steps list.
1956
- {
1957
- headers,
1958
- method: "POST",
1959
- body: JSON.stringify(singleStep),
1960
- url: this.context.url,
1961
- notBefore: willWait ? singleStep.sleepUntil : void 0,
1962
- delay: willWait ? singleStep.sleepFor : void 0
1963
- }
1964
- );
1965
- })
1966
- );
1967
- const _result = result;
1968
- await this.debug?.log("INFO", "SUBMIT_STEP", {
1969
- messageIds: _result.map((message) => {
1970
- return {
1971
- message: message.messageId
1972
- };
1973
- })
1974
- });
1975
- throw new WorkflowAbort(steps[0].stepName, steps[0]);
1976
- }
1977
2168
  /**
1978
2169
  * Get the promise by executing the lazt steps list. If there is a single
1979
2170
  * step, we call `runSingle`. Otherwise `runParallel` is called.
@@ -2169,6 +2360,9 @@ var WorkflowApi = class extends BaseWorkflowApi {
2169
2360
  }
2170
2361
  };
2171
2362
 
2363
+ // src/agents/index.ts
2364
+ import { createOpenAI } from "@ai-sdk/openai";
2365
+
2172
2366
  // src/agents/agent.ts
2173
2367
  import { z } from "zod";
2174
2368
  import { generateText, tool as tool2, ToolExecutionError } from "ai";
@@ -2373,10 +2567,16 @@ var WorkflowAgents = class {
2373
2567
  */
2374
2568
  openai(...params) {
2375
2569
  const [model, settings] = params;
2376
- const { baseURL, apiKey, ...otherSettings } = settings ?? {};
2377
- const openai2 = createWorkflowOpenAI(this.context, { baseURL, apiKey });
2378
- return openai2(model, otherSettings);
2570
+ const { baseURL, apiKey, callSettings, ...otherSettings } = settings ?? {};
2571
+ const openaiModel = this.AISDKModel({
2572
+ context: this.context,
2573
+ provider: createOpenAI,
2574
+ providerParams: { baseURL, apiKey, compatibility: "strict" },
2575
+ agentCallParams: callSettings
2576
+ });
2577
+ return openaiModel(model, otherSettings);
2379
2578
  }
2579
+ AISDKModel = createWorkflowModel;
2380
2580
  };
2381
2581
 
2382
2582
  // src/context/context.ts
@@ -2600,60 +2800,42 @@ var WorkflowContext = class {
2600
2800
  }
2601
2801
  await this.addStep(new LazySleepUntilStep(stepName, time));
2602
2802
  }
2603
- /**
2604
- * Makes a third party call through QStash in order to make a
2605
- * network call without consuming any runtime.
2606
- *
2607
- * ```ts
2608
- * const { status, body } = await context.call<string>(
2609
- * "post call step",
2610
- * {
2611
- * url: "https://www.some-endpoint.com/api",
2612
- * method: "POST",
2613
- * body: "my-payload"
2614
- * }
2615
- * );
2616
- * ```
2617
- *
2618
- * tries to parse the result of the request as JSON. If it's
2619
- * not a JSON which can be parsed, simply returns the response
2620
- * body as it is.
2621
- *
2622
- * @param stepName
2623
- * @param url url to call
2624
- * @param method call method. "GET" by default.
2625
- * @param body call body
2626
- * @param headers call headers
2627
- * @param retries number of call retries. 0 by default
2628
- * @param timeout max duration to wait for the endpoint to respond. in seconds.
2629
- * @returns call result as {
2630
- * status: number;
2631
- * body: unknown;
2632
- * header: Record<string, string[]>
2633
- * }
2634
- */
2635
2803
  async call(stepName, settings) {
2636
- const {
2637
- url,
2638
- method = "GET",
2639
- body: requestBody,
2640
- headers = {},
2641
- retries = 0,
2642
- timeout,
2643
- flowControl
2644
- } = settings;
2645
- return await this.addStep(
2646
- new LazyCallStep(
2804
+ let callStep;
2805
+ if ("workflow" in settings) {
2806
+ const url = getNewUrlFromWorkflowId(this.url, settings.workflow.workflowId);
2807
+ callStep = new LazyCallStep(
2808
+ stepName,
2809
+ url,
2810
+ "POST",
2811
+ settings.body,
2812
+ settings.headers || {},
2813
+ settings.retries || 0,
2814
+ settings.timeout,
2815
+ settings.flowControl ?? settings.workflow.options.flowControl
2816
+ );
2817
+ } else {
2818
+ const {
2819
+ url,
2820
+ method = "GET",
2821
+ body,
2822
+ headers = {},
2823
+ retries = 0,
2824
+ timeout,
2825
+ flowControl
2826
+ } = settings;
2827
+ callStep = new LazyCallStep(
2647
2828
  stepName,
2648
2829
  url,
2649
2830
  method,
2650
- requestBody,
2831
+ body,
2651
2832
  headers,
2652
2833
  retries,
2653
2834
  timeout,
2654
2835
  flowControl
2655
- )
2656
- );
2836
+ );
2837
+ }
2838
+ return await this.addStep(callStep);
2657
2839
  }
2658
2840
  /**
2659
2841
  * Pauses workflow execution until a specific event occurs or a timeout is reached.
@@ -3099,6 +3281,7 @@ var processOptions = (options) => {
3099
3281
  retries: DEFAULT_RETRIES,
3100
3282
  useJSONContent: false,
3101
3283
  disableTelemetry: false,
3284
+ onError: console.error,
3102
3285
  ...options
3103
3286
  };
3104
3287
  };
@@ -3148,7 +3331,8 @@ var serveBase = (routeFunction, telemetry, options) => {
3148
3331
  retries,
3149
3332
  useJSONContent,
3150
3333
  disableTelemetry,
3151
- flowControl
3334
+ flowControl,
3335
+ onError
3152
3336
  } = processOptions(options);
3153
3337
  telemetry = disableTelemetry ? void 0 : telemetry;
3154
3338
  const debug = WorkflowLogger.getLogger(verbose);
@@ -3277,8 +3461,19 @@ var serveBase = (routeFunction, telemetry, options) => {
3277
3461
  try {
3278
3462
  return await handler(request);
3279
3463
  } catch (error) {
3280
- console.error(error);
3281
- return new Response(JSON.stringify(formatWorkflowError(error)), {
3464
+ const formattedError = formatWorkflowError(error);
3465
+ try {
3466
+ onError?.(error);
3467
+ } catch (onErrorError) {
3468
+ const formattedOnErrorError = formatWorkflowError(onErrorError);
3469
+ const errorMessage = `Error while running onError callback: '${formattedOnErrorError.message}'.
3470
+ Original error: '${formattedError.message}'`;
3471
+ console.error(errorMessage);
3472
+ return new Response(errorMessage, {
3473
+ status: 500
3474
+ });
3475
+ }
3476
+ return new Response(JSON.stringify(formattedError), {
3282
3477
  status: 500
3283
3478
  });
3284
3479
  }
@@ -3308,8 +3503,8 @@ export {
3308
3503
  getWorkflowRunId,
3309
3504
  StepTypes,
3310
3505
  triggerFirstInvocation,
3311
- serveManyBase,
3312
3506
  WorkflowTool,
3507
+ serveManyBase,
3313
3508
  WorkflowContext,
3314
3509
  WorkflowLogger,
3315
3510
  serveBase,