@mastra/client-js 0.0.0-agui-20250501191909 → 0.0.0-cli-debug-20250611094457

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.
@@ -1,6 +1,8 @@
1
+ import type { RuntimeContext } from '@mastra/core/runtime-context';
1
2
  import type { GetToolResponse, ClientOptions } from '../types';
2
3
 
3
4
  import { BaseResource } from './base';
5
+ import { parseClientRuntimeContext } from '../utils';
4
6
 
5
7
  export class Tool extends BaseResource {
6
8
  constructor(
@@ -23,16 +25,21 @@ export class Tool extends BaseResource {
23
25
  * @param params - Parameters required for tool execution
24
26
  * @returns Promise containing the tool execution results
25
27
  */
26
- execute(params: { data: any; runId?: string }): Promise<any> {
28
+ execute(params: { data: any; runId?: string; runtimeContext?: RuntimeContext | Record<string, any> }): Promise<any> {
27
29
  const url = new URLSearchParams();
28
30
 
29
31
  if (params.runId) {
30
32
  url.set('runId', params.runId);
31
33
  }
32
34
 
35
+ const body = {
36
+ data: params.data,
37
+ runtimeContext: parseClientRuntimeContext(params.runtimeContext),
38
+ };
39
+
33
40
  return this.request(`/api/tools/${this.toolId}/execute?${url.toString()}`, {
34
41
  method: 'POST',
35
- body: params.data,
42
+ body,
36
43
  });
37
44
  }
38
45
  }
@@ -1,5 +1,14 @@
1
- import type { GetWorkflowResponse, ClientOptions, WorkflowRunResult, GetWorkflowRunsResponse } from '../types';
1
+ import type { RuntimeContext } from '@mastra/core/runtime-context';
2
+ import type {
3
+ ClientOptions,
4
+ GetWorkflowResponse,
5
+ GetWorkflowRunsResponse,
6
+ GetWorkflowRunsParams,
7
+ WorkflowRunResult,
8
+ WorkflowWatchResult,
9
+ } from '../types';
2
10
 
11
+ import { parseClientRuntimeContext } from '../utils';
3
12
  import { BaseResource } from './base';
4
13
 
5
14
  const RECORD_SEPARATOR = '\x1E';
@@ -12,6 +21,77 @@ export class Workflow extends BaseResource {
12
21
  super(options);
13
22
  }
14
23
 
24
+ /**
25
+ * Creates an async generator that processes a readable stream and yields workflow records
26
+ * separated by the Record Separator character (\x1E)
27
+ *
28
+ * @param stream - The readable stream to process
29
+ * @returns An async generator that yields parsed records
30
+ */
31
+ private async *streamProcessor(stream: ReadableStream): AsyncGenerator<WorkflowWatchResult, void, unknown> {
32
+ const reader = stream.getReader();
33
+
34
+ // Track if we've finished reading from the stream
35
+ let doneReading = false;
36
+ // Buffer to accumulate partial chunks
37
+ let buffer = '';
38
+
39
+ try {
40
+ while (!doneReading) {
41
+ // Read the next chunk from the stream
42
+ const { done, value } = await reader.read();
43
+ doneReading = done;
44
+
45
+ // Skip processing if we're done and there's no value
46
+ if (done && !value) continue;
47
+
48
+ try {
49
+ // Decode binary data to text
50
+ const decoded = value ? new TextDecoder().decode(value) : '';
51
+
52
+ // Split the combined buffer and new data by record separator
53
+ const chunks = (buffer + decoded).split(RECORD_SEPARATOR);
54
+
55
+ // The last chunk might be incomplete, so save it for the next iteration
56
+ buffer = chunks.pop() || '';
57
+
58
+ // Process complete chunks
59
+ for (const chunk of chunks) {
60
+ if (chunk) {
61
+ // Only process non-empty chunks
62
+ if (typeof chunk === 'string') {
63
+ try {
64
+ const parsedChunk = JSON.parse(chunk);
65
+ yield parsedChunk;
66
+ } catch {
67
+ // Silently ignore parsing errors to maintain stream processing
68
+ // This allows the stream to continue even if one record is malformed
69
+ }
70
+ }
71
+ }
72
+ }
73
+ } catch {
74
+ // Silently ignore parsing errors to maintain stream processing
75
+ // This allows the stream to continue even if one record is malformed
76
+ }
77
+ }
78
+
79
+ // Process any remaining data in the buffer after stream is done
80
+ if (buffer) {
81
+ try {
82
+ yield JSON.parse(buffer);
83
+ } catch {
84
+ // Ignore parsing error for final chunk
85
+ }
86
+ }
87
+ } finally {
88
+ // Always ensure we clean up the reader
89
+ reader.cancel().catch(() => {
90
+ // Ignore cancel errors
91
+ });
92
+ }
93
+ }
94
+
15
95
  /**
16
96
  * Retrieves details about the workflow
17
97
  * @returns Promise containing workflow details including steps and graphs
@@ -22,28 +102,38 @@ export class Workflow extends BaseResource {
22
102
 
23
103
  /**
24
104
  * Retrieves all runs for a workflow
105
+ * @param params - Parameters for filtering runs
25
106
  * @returns Promise containing workflow runs array
26
107
  */
27
- runs(): Promise<GetWorkflowRunsResponse> {
28
- return this.request(`/api/workflows/${this.workflowId}/runs`);
29
- }
108
+ runs(params?: GetWorkflowRunsParams): Promise<GetWorkflowRunsResponse> {
109
+ const searchParams = new URLSearchParams();
110
+ if (params?.fromDate) {
111
+ searchParams.set('fromDate', params.fromDate.toISOString());
112
+ }
113
+ if (params?.toDate) {
114
+ searchParams.set('toDate', params.toDate.toISOString());
115
+ }
116
+ if (params?.limit) {
117
+ searchParams.set('limit', String(params.limit));
118
+ }
119
+ if (params?.offset) {
120
+ searchParams.set('offset', String(params.offset));
121
+ }
122
+ if (params?.resourceId) {
123
+ searchParams.set('resourceId', params.resourceId);
124
+ }
30
125
 
31
- /**
32
- * @deprecated Use `startAsync` instead
33
- * Executes the workflow with the provided parameters
34
- * @param params - Parameters required for workflow execution
35
- * @returns Promise containing the workflow execution results
36
- */
37
- execute(params: Record<string, any>): Promise<WorkflowRunResult> {
38
- return this.request(`/api/workflows/${this.workflowId}/execute`, {
39
- method: 'POST',
40
- body: params,
41
- });
126
+ if (searchParams.size) {
127
+ return this.request(`/api/workflows/${this.workflowId}/runs?${searchParams}`);
128
+ } else {
129
+ return this.request(`/api/workflows/${this.workflowId}/runs`);
130
+ }
42
131
  }
43
132
 
44
133
  /**
45
134
  * Creates a new workflow run
46
- * @returns Promise containing the generated run ID
135
+ * @param params - Optional object containing the optional runId
136
+ * @returns Promise containing the runId of the created run
47
137
  */
48
138
  createRun(params?: { runId?: string }): Promise<{ runId: string }> {
49
139
  const searchParams = new URLSearchParams();
@@ -52,150 +142,162 @@ export class Workflow extends BaseResource {
52
142
  searchParams.set('runId', params.runId);
53
143
  }
54
144
 
55
- return this.request(`/api/workflows/${this.workflowId}/createRun?${searchParams.toString()}`, {
145
+ return this.request(`/api/workflows/${this.workflowId}/create-run?${searchParams.toString()}`, {
56
146
  method: 'POST',
57
147
  });
58
148
  }
59
149
 
60
150
  /**
61
151
  * Starts a workflow run synchronously without waiting for the workflow to complete
62
- * @param params - Object containing the runId and triggerData
152
+ * @param params - Object containing the runId, inputData and runtimeContext
63
153
  * @returns Promise containing success message
64
154
  */
65
- start(params: { runId: string; triggerData: Record<string, any> }): Promise<{ message: string }> {
155
+ start(params: {
156
+ runId: string;
157
+ inputData: Record<string, any>;
158
+ runtimeContext?: RuntimeContext | Record<string, any>;
159
+ }): Promise<{ message: string }> {
160
+ const runtimeContext = parseClientRuntimeContext(params.runtimeContext);
66
161
  return this.request(`/api/workflows/${this.workflowId}/start?runId=${params.runId}`, {
67
162
  method: 'POST',
68
- body: params?.triggerData,
163
+ body: { inputData: params?.inputData, runtimeContext },
69
164
  });
70
165
  }
71
166
 
72
167
  /**
73
168
  * Resumes a suspended workflow step synchronously without waiting for the workflow to complete
74
- * @param stepId - ID of the step to resume
75
- * @param runId - ID of the workflow run
76
- * @param context - Context to resume the workflow with
77
- * @returns Promise containing the workflow resume results
169
+ * @param params - Object containing the runId, step, resumeData and runtimeContext
170
+ * @returns Promise containing success message
78
171
  */
79
172
  resume({
80
- stepId,
173
+ step,
81
174
  runId,
82
- context,
175
+ resumeData,
176
+ ...rest
83
177
  }: {
84
- stepId: string;
178
+ step: string | string[];
85
179
  runId: string;
86
- context: Record<string, any>;
180
+ resumeData?: Record<string, any>;
181
+ runtimeContext?: RuntimeContext | Record<string, any>;
87
182
  }): Promise<{ message: string }> {
183
+ const runtimeContext = parseClientRuntimeContext(rest.runtimeContext);
88
184
  return this.request(`/api/workflows/${this.workflowId}/resume?runId=${runId}`, {
89
185
  method: 'POST',
186
+ stream: true,
90
187
  body: {
91
- stepId,
92
- context,
188
+ step,
189
+ resumeData,
190
+ runtimeContext,
93
191
  },
94
192
  });
95
193
  }
96
194
 
97
195
  /**
98
196
  * Starts a workflow run asynchronously and returns a promise that resolves when the workflow is complete
99
- * @param params - Object containing the optional runId and triggerData
197
+ * @param params - Object containing the optional runId, inputData and runtimeContext
100
198
  * @returns Promise containing the workflow execution results
101
199
  */
102
- startAsync(params: { runId?: string; triggerData: Record<string, any> }): Promise<WorkflowRunResult> {
200
+ startAsync(params: {
201
+ runId?: string;
202
+ inputData: Record<string, any>;
203
+ runtimeContext?: RuntimeContext | Record<string, any>;
204
+ }): Promise<WorkflowRunResult> {
103
205
  const searchParams = new URLSearchParams();
104
206
 
105
207
  if (!!params?.runId) {
106
208
  searchParams.set('runId', params.runId);
107
209
  }
108
210
 
211
+ const runtimeContext = parseClientRuntimeContext(params.runtimeContext);
212
+
109
213
  return this.request(`/api/workflows/${this.workflowId}/start-async?${searchParams.toString()}`, {
110
214
  method: 'POST',
111
- body: params?.triggerData,
215
+ body: { inputData: params.inputData, runtimeContext },
112
216
  });
113
217
  }
114
218
 
115
219
  /**
116
- * Resumes a suspended workflow step asynchronously and returns a promise that resolves when the workflow is complete
117
- * @param params - Object containing the runId, stepId, and context
118
- * @returns Promise containing the workflow resume results
220
+ * Starts a vNext workflow run and returns a stream
221
+ * @param params - Object containing the optional runId, inputData and runtimeContext
222
+ * @returns Promise containing the vNext workflow execution results
119
223
  */
120
- resumeAsync(params: { runId: string; stepId: string; context: Record<string, any> }): Promise<WorkflowRunResult> {
121
- return this.request(`/api/workflows/${this.workflowId}/resume-async?runId=${params.runId}`, {
122
- method: 'POST',
123
- body: {
124
- stepId: params.stepId,
125
- context: params.context,
126
- },
127
- });
128
- }
224
+ async stream(params: { runId?: string; inputData: Record<string, any>; runtimeContext?: RuntimeContext }) {
225
+ const searchParams = new URLSearchParams();
129
226
 
130
- /**
131
- * Creates an async generator that processes a readable stream and yields records
132
- * separated by the Record Separator character (\x1E)
133
- *
134
- * @param stream - The readable stream to process
135
- * @returns An async generator that yields parsed records
136
- */
137
- private async *streamProcessor(stream: ReadableStream): AsyncGenerator<WorkflowRunResult, void, unknown> {
138
- const reader = stream.getReader();
227
+ if (!!params?.runId) {
228
+ searchParams.set('runId', params.runId);
229
+ }
139
230
 
140
- // Track if we've finished reading from the stream
141
- let doneReading = false;
142
- // Buffer to accumulate partial chunks
143
- let buffer = '';
231
+ const runtimeContext = params.runtimeContext ? Object.fromEntries(params.runtimeContext.entries()) : undefined;
232
+ const response: Response = await this.request(
233
+ `/api/workflows/${this.workflowId}/stream?${searchParams.toString()}`,
234
+ {
235
+ method: 'POST',
236
+ body: { inputData: params.inputData, runtimeContext },
237
+ stream: true,
238
+ },
239
+ );
144
240
 
145
- try {
146
- while (!doneReading) {
147
- // Read the next chunk from the stream
148
- const { done, value } = await reader.read();
149
- doneReading = done;
241
+ if (!response.ok) {
242
+ throw new Error(`Failed to stream vNext workflow: ${response.statusText}`);
243
+ }
150
244
 
151
- // Skip processing if we're done and there's no value
152
- if (done && !value) continue;
245
+ if (!response.body) {
246
+ throw new Error('Response body is null');
247
+ }
153
248
 
249
+ // Create a transform stream that processes the response body
250
+ const transformStream = new TransformStream<ArrayBuffer, WorkflowWatchResult>({
251
+ start() {},
252
+ async transform(chunk, controller) {
154
253
  try {
155
254
  // Decode binary data to text
156
- const decoded = value ? new TextDecoder().decode(value) : '';
255
+ const decoded = new TextDecoder().decode(chunk);
157
256
 
158
- // Split the combined buffer and new data by record separator
159
- const chunks = (buffer + decoded).split(RECORD_SEPARATOR);
160
-
161
- // The last chunk might be incomplete, so save it for the next iteration
162
- buffer = chunks.pop() || '';
257
+ // Split by record separator
258
+ const chunks = decoded.split(RECORD_SEPARATOR);
163
259
 
164
- // Process complete chunks
260
+ // Process each chunk
165
261
  for (const chunk of chunks) {
166
262
  if (chunk) {
167
- // Only process non-empty chunks
168
- if (typeof chunk === 'string') {
169
- try {
170
- const parsedChunk = JSON.parse(chunk);
171
- yield parsedChunk;
172
- } catch {
173
- // Silently ignore parsing errors to maintain stream processing
174
- // This allows the stream to continue even if one record is malformed
175
- }
263
+ try {
264
+ const parsedChunk = JSON.parse(chunk);
265
+ controller.enqueue(parsedChunk);
266
+ } catch {
267
+ // Silently ignore parsing errors
176
268
  }
177
269
  }
178
270
  }
179
271
  } catch {
180
- // Silently ignore parsing errors to maintain stream processing
181
- // This allows the stream to continue even if one record is malformed
272
+ // Silently ignore processing errors
182
273
  }
183
- }
274
+ },
275
+ });
184
276
 
185
- // Process any remaining data in the buffer after stream is done
186
- if (buffer) {
187
- try {
188
- yield JSON.parse(buffer);
189
- } catch {
190
- // Ignore parsing error for final chunk
191
- }
192
- }
193
- } finally {
194
- // Always ensure we clean up the reader
195
- reader.cancel().catch(() => {
196
- // Ignore cancel errors
197
- });
198
- }
277
+ // Pipe the response body through the transform stream
278
+ return response.body.pipeThrough(transformStream);
279
+ }
280
+
281
+ /**
282
+ * Resumes a suspended workflow step asynchronously and returns a promise that resolves when the workflow is complete
283
+ * @param params - Object containing the runId, step, resumeData and runtimeContext
284
+ * @returns Promise containing the workflow resume results
285
+ */
286
+ resumeAsync(params: {
287
+ runId: string;
288
+ step: string | string[];
289
+ resumeData?: Record<string, any>;
290
+ runtimeContext?: RuntimeContext | Record<string, any>;
291
+ }): Promise<WorkflowRunResult> {
292
+ const runtimeContext = parseClientRuntimeContext(params.runtimeContext);
293
+ return this.request(`/api/workflows/${this.workflowId}/resume-async?runId=${params.runId}`, {
294
+ method: 'POST',
295
+ body: {
296
+ step: params.step,
297
+ resumeData: params.resumeData,
298
+ runtimeContext,
299
+ },
300
+ });
199
301
  }
200
302
 
201
303
  /**
@@ -203,7 +305,7 @@ export class Workflow extends BaseResource {
203
305
  * @param runId - Optional run ID to filter the watch stream
204
306
  * @returns AsyncGenerator that yields parsed records from the workflow watch stream
205
307
  */
206
- async watch({ runId }: { runId?: string }, onRecord: (record: WorkflowRunResult) => void) {
308
+ async watch({ runId }: { runId?: string }, onRecord: (record: WorkflowWatchResult) => void) {
207
309
  const response: Response = await this.request(`/api/workflows/${this.workflowId}/watch?runId=${runId}`, {
208
310
  stream: true,
209
311
  });
@@ -217,7 +319,35 @@ export class Workflow extends BaseResource {
217
319
  }
218
320
 
219
321
  for await (const record of this.streamProcessor(response.body)) {
220
- onRecord(record);
322
+ if (typeof record === 'string') {
323
+ onRecord(JSON.parse(record));
324
+ } else {
325
+ onRecord(record);
326
+ }
221
327
  }
222
328
  }
329
+
330
+ /**
331
+ * Creates a new ReadableStream from an iterable or async iterable of objects,
332
+ * serializing each as JSON and separating them with the record separator (\x1E).
333
+ *
334
+ * @param records - An iterable or async iterable of objects to stream
335
+ * @returns A ReadableStream emitting the records as JSON strings separated by the record separator
336
+ */
337
+ static createRecordStream(records: Iterable<any> | AsyncIterable<any>): ReadableStream {
338
+ const encoder = new TextEncoder();
339
+ return new ReadableStream({
340
+ async start(controller) {
341
+ try {
342
+ for await (const record of records as AsyncIterable<any>) {
343
+ const json = JSON.stringify(record) + RECORD_SEPARATOR;
344
+ controller.enqueue(encoder.encode(json));
345
+ }
346
+ controller.close();
347
+ } catch (err) {
348
+ controller.error(err);
349
+ }
350
+ },
351
+ });
352
+ }
223
353
  }