@mastra/mcp-docs-server 1.0.0-beta.10 → 1.0.0-beta.11

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.
Files changed (28) hide show
  1. package/.docs/organized/changelogs/%40mastra%2Fai-sdk.md +27 -27
  2. package/.docs/organized/changelogs/%40mastra%2Fchroma.md +10 -10
  3. package/.docs/organized/changelogs/%40mastra%2Fclient-js.md +27 -27
  4. package/.docs/organized/changelogs/%40mastra%2Fcore.md +91 -91
  5. package/.docs/organized/changelogs/%40mastra%2Fdeployer-cloud.md +9 -9
  6. package/.docs/organized/changelogs/%40mastra%2Fdeployer.md +11 -11
  7. package/.docs/organized/changelogs/%40mastra%2Fmcp-docs-server.md +8 -8
  8. package/.docs/organized/changelogs/%40mastra%2Fplayground-ui.md +30 -30
  9. package/.docs/organized/changelogs/%40mastra%2Freact.md +26 -0
  10. package/.docs/organized/changelogs/%40mastra%2Fserver.md +31 -31
  11. package/.docs/organized/changelogs/create-mastra.md +3 -3
  12. package/.docs/organized/changelogs/mastra.md +15 -15
  13. package/.docs/raw/agents/guardrails.mdx +43 -6
  14. package/.docs/raw/agents/processors.mdx +151 -0
  15. package/.docs/raw/getting-started/mcp-docs-server.mdx +57 -0
  16. package/.docs/raw/getting-started/studio.mdx +24 -1
  17. package/.docs/raw/guides/migrations/upgrade-to-v1/agent.mdx +70 -0
  18. package/.docs/raw/reference/agents/agent.mdx +11 -4
  19. package/.docs/raw/reference/core/getServer.mdx +1 -1
  20. package/.docs/raw/reference/processors/processor-interface.mdx +314 -13
  21. package/.docs/raw/reference/streaming/ChunkType.mdx +23 -2
  22. package/.docs/raw/reference/streaming/agents/stream.mdx +16 -29
  23. package/.docs/raw/reference/workflows/workflow-methods/foreach.mdx +68 -3
  24. package/.docs/raw/reference/workflows/workflow.mdx +23 -0
  25. package/.docs/raw/server-db/mastra-server.mdx +7 -5
  26. package/.docs/raw/workflows/control-flow.mdx +348 -2
  27. package/CHANGELOG.md +7 -0
  28. package/package.json +5 -5
@@ -9,7 +9,7 @@ The `Processor` interface defines the contract for all processors in Mastra. Pro
9
9
 
10
10
  ## When processor methods run
11
11
 
12
- The four processor methods run at different points in the agent execution lifecycle:
12
+ The five processor methods run at different points in the agent execution lifecycle:
13
13
 
14
14
  ```
15
15
  ┌─────────────────────────────────────────────────────────────────┐
@@ -39,6 +39,11 @@ The four processor methods run at different points in the agent execution lifecy
39
39
  │ │ └──────────┬───────────┘ │ │
40
40
  │ │ │ │ │
41
41
  │ │ ▼ │ │
42
+ │ │ ┌──────────────────────┐ │ │
43
+ │ │ │ processOutputStep │ ← Runs after EACH LLM step │ │
44
+ │ │ └──────────┬───────────┘ │ │
45
+ │ │ │ │ │
46
+ │ │ ▼ │ │
42
47
  │ │ Tool Execution (if needed) │ │
43
48
  │ │ │ │ │
44
49
  │ │ └──────── Loop back if tools called ────────│ │
@@ -60,6 +65,7 @@ The four processor methods run at different points in the agent execution lifecy
60
65
  | `processInput` | Once at the start, before the agentic loop | Validate/transform initial user input, add context |
61
66
  | `processInputStep` | At each step of the agentic loop, before each LLM call | Transform messages between steps, handle tool results |
62
67
  | `processOutputStream` | On each streaming chunk during LLM response | Filter/modify streaming content, detect patterns in real-time |
68
+ | `processOutputStep` | After each LLM response, before tool execution | Validate output quality, implement guardrails with retry |
63
69
  | `processOutputResult` | Once after generation completes | Post-process final response, log results |
64
70
 
65
71
  ## Interface definition
@@ -70,9 +76,10 @@ interface Processor<TId extends string = string> {
70
76
  readonly name?: string;
71
77
 
72
78
  processInput?(args: ProcessInputArgs): Promise<ProcessInputResult> | ProcessInputResult;
79
+ processInputStep?(args: ProcessInputStepArgs): ProcessorMessageResult;
73
80
  processOutputStream?(args: ProcessOutputStreamArgs): Promise<ChunkType | null | undefined>;
81
+ processOutputStep?(args: ProcessOutputStepArgs): ProcessorMessageResult;
74
82
  processOutputResult?(args: ProcessOutputResultArgs): ProcessorMessageResult;
75
- processInputStep?(args: ProcessInputStepArgs): ProcessorMessageResult;
76
83
  }
77
84
  ```
78
85
 
@@ -129,10 +136,16 @@ processInput?(args: ProcessInputArgs): Promise<ProcessInputResult> | ProcessInpu
129
136
  },
130
137
  {
131
138
  name: "abort",
132
- type: "(reason?: string) => never",
133
- description: "Function to abort processing. Throws a TripWire error that stops execution.",
139
+ type: "(reason?: string, options?: { retry?: boolean; metadata?: unknown }) => never",
140
+ description: "Function to abort processing. Throws a TripWire error that stops execution. Pass `retry: true` to request the LLM retry the step with feedback.",
134
141
  isOptional: false,
135
142
  },
143
+ {
144
+ name: "retryCount",
145
+ type: "number",
146
+ description: "Number of times processors have triggered retry for this generation. Use this to limit retry attempts.",
147
+ isOptional: true,
148
+ },
136
149
  {
137
150
  name: "tracingContext",
138
151
  type: "TracingContext",
@@ -185,8 +198,8 @@ processInputStep?(args: ProcessInputStepArgs): ProcessorMessageResult;
185
198
  #### Execution order in the agentic loop
186
199
 
187
200
  1. `processInput` (once at start)
188
- 2. `processInputStep` (at each step, before LLM call)
189
- 3. `prepareStep` callback (if provided)
201
+ 2. `processInputStep` from inputProcessors (at each step, before LLM call)
202
+ 3. `prepareStep` callback (runs as part of the processInputStep pipeline, after inputProcessors)
190
203
  4. LLM execution
191
204
  5. Tool execution (if needed)
192
205
  6. Repeat from step 2 if tools were called
@@ -198,13 +211,13 @@ processInputStep?(args: ProcessInputStepArgs): ProcessorMessageResult;
198
211
  {
199
212
  name: "messages",
200
213
  type: "MastraDBMessage[]",
201
- description: "All messages including tool calls and results from previous steps.",
214
+ description: "All messages including tool calls and results from previous steps (read-only snapshot).",
202
215
  isOptional: false,
203
216
  },
204
217
  {
205
218
  name: "messageList",
206
219
  type: "MessageList",
207
- description: "MessageList instance for managing messages.",
220
+ description: "MessageList instance for managing messages. Can mutate directly or return in result.",
208
221
  isOptional: false,
209
222
  },
210
223
  {
@@ -213,12 +226,60 @@ processInputStep?(args: ProcessInputStepArgs): ProcessorMessageResult;
213
226
  description: "Current step number (0-indexed). Step 0 is the initial LLM call.",
214
227
  isOptional: false,
215
228
  },
229
+ {
230
+ name: "steps",
231
+ type: "StepResult[]",
232
+ description: "Results from previous steps, including text, toolCalls, and toolResults.",
233
+ isOptional: false,
234
+ },
216
235
  {
217
236
  name: "systemMessages",
218
237
  type: "CoreMessage[]",
219
- description: "All system messages for read/modify access.",
238
+ description: "All system messages (read-only snapshot). Return in result to replace.",
220
239
  isOptional: false,
221
240
  },
241
+ {
242
+ name: "model",
243
+ type: "MastraLanguageModelV2",
244
+ description: "Current model being used. Return a different model in result to switch.",
245
+ isOptional: false,
246
+ },
247
+ {
248
+ name: "toolChoice",
249
+ type: "ToolChoice",
250
+ description: "Current tool choice setting ('auto', 'none', 'required', or specific tool).",
251
+ isOptional: true,
252
+ },
253
+ {
254
+ name: "activeTools",
255
+ type: "string[]",
256
+ description: "Currently active tool names. Return filtered array to limit tools.",
257
+ isOptional: true,
258
+ },
259
+ {
260
+ name: "tools",
261
+ type: "ToolSet",
262
+ description: "Current tools available for this step. Return in result to add/replace tools.",
263
+ isOptional: true,
264
+ },
265
+ {
266
+ name: "providerOptions",
267
+ type: "SharedV2ProviderOptions",
268
+ description: "Provider-specific options (e.g., Anthropic cacheControl, OpenAI reasoningEffort).",
269
+ isOptional: true,
270
+ },
271
+ {
272
+ name: "modelSettings",
273
+ type: "CallSettings",
274
+ description: "Model settings like temperature, maxTokens, topP.",
275
+ isOptional: true,
276
+ },
277
+ {
278
+ name: "structuredOutput",
279
+ type: "StructuredOutputOptions",
280
+ description: "Structured output configuration (schema, output mode). Return in result to modify.",
281
+ isOptional: true,
282
+ },
222
283
  {
223
284
  name: "abort",
224
285
  type: "(reason?: string) => never",
@@ -240,12 +301,99 @@ processInputStep?(args: ProcessInputStepArgs): ProcessorMessageResult;
240
301
  ]}
241
302
  />
242
303
 
304
+ #### ProcessInputStepResult
305
+
306
+ The method can return any combination of these properties:
307
+
308
+ <PropertiesTable
309
+ content={[
310
+ {
311
+ name: "model",
312
+ type: "LanguageModelV2 | string",
313
+ description: "Change the model for this step. Can be a model instance or router ID like 'openai/gpt-4o'.",
314
+ isOptional: true,
315
+ },
316
+ {
317
+ name: "toolChoice",
318
+ type: "ToolChoice",
319
+ description: "Change tool selection behavior for this step.",
320
+ isOptional: true,
321
+ },
322
+ {
323
+ name: "activeTools",
324
+ type: "string[]",
325
+ description: "Filter which tools are available for this step.",
326
+ isOptional: true,
327
+ },
328
+ {
329
+ name: "tools",
330
+ type: "ToolSet",
331
+ description: "Replace or modify tools for this step. Use spread to merge: { tools: { ...tools, newTool } }.",
332
+ isOptional: true,
333
+ },
334
+ {
335
+ name: "messages",
336
+ type: "MastraDBMessage[]",
337
+ description: "Replace all messages. Cannot be used with messageList.",
338
+ isOptional: true,
339
+ },
340
+ {
341
+ name: "messageList",
342
+ type: "MessageList",
343
+ description: "Return the same messageList instance (indicates you mutated it). Cannot be used with messages.",
344
+ isOptional: true,
345
+ },
346
+ {
347
+ name: "systemMessages",
348
+ type: "CoreMessage[]",
349
+ description: "Replace all system messages for this step only.",
350
+ isOptional: true,
351
+ },
352
+ {
353
+ name: "providerOptions",
354
+ type: "SharedV2ProviderOptions",
355
+ description: "Change provider-specific options for this step.",
356
+ isOptional: true,
357
+ },
358
+ {
359
+ name: "modelSettings",
360
+ type: "CallSettings",
361
+ description: "Change model settings for this step.",
362
+ isOptional: true,
363
+ },
364
+ {
365
+ name: "structuredOutput",
366
+ type: "StructuredOutputOptions",
367
+ description: "Change structured output configuration for this step.",
368
+ isOptional: true,
369
+ },
370
+ ]}
371
+ />
372
+
373
+ #### Processor chaining
374
+
375
+ When multiple processors implement `processInputStep`, they run in order and changes chain through:
376
+
377
+ ```
378
+ Processor 1: receives { model: 'gpt-4o' } → returns { model: 'gpt-4o-mini' }
379
+ Processor 2: receives { model: 'gpt-4o-mini' } → returns { toolChoice: 'none' }
380
+ Final: model = 'gpt-4o-mini', toolChoice = 'none'
381
+ ```
382
+
383
+ #### System message isolation
384
+
385
+ System messages are **reset to their original values** at the start of each step. Modifications made in `processInputStep` only affect the current step, not subsequent steps.
386
+
243
387
  #### Use cases
244
388
 
389
+ - Dynamic model switching based on step number or context
390
+ - Disabling tools after a certain number of steps
391
+ - Dynamically adding or replacing tools based on conversation context
245
392
  - Transforming message part types between providers (e.g., `reasoning` → `thinking` for Anthropic)
246
393
  - Modifying messages based on step number or accumulated context
247
- - Implementing per-step message filtering or enrichment
248
- - Adding step-specific context or instructions
394
+ - Adding step-specific system instructions
395
+ - Adjusting provider options per step (e.g., cache control)
396
+ - Modifying structured output schema based on step context
249
397
 
250
398
  ---
251
399
 
@@ -358,6 +506,125 @@ processOutputResult?(args: ProcessOutputResultArgs): ProcessorMessageResult;
358
506
  ]}
359
507
  />
360
508
 
509
+ ---
510
+
511
+ ### processOutputStep
512
+
513
+ Processes output after each LLM response in the agentic loop, before tool execution. Unlike `processOutputResult` which runs once at the end, this runs at every step. This is the ideal method for implementing guardrails that can trigger retries.
514
+
515
+ ```typescript copy
516
+ processOutputStep?(args: ProcessOutputStepArgs): ProcessorMessageResult;
517
+ ```
518
+
519
+ #### ProcessOutputStepArgs
520
+
521
+ <PropertiesTable
522
+ content={[
523
+ {
524
+ name: "messages",
525
+ type: "MastraDBMessage[]",
526
+ description: "All messages including the latest LLM response.",
527
+ isOptional: false,
528
+ },
529
+ {
530
+ name: "messageList",
531
+ type: "MessageList",
532
+ description: "MessageList instance for managing messages.",
533
+ isOptional: false,
534
+ },
535
+ {
536
+ name: "stepNumber",
537
+ type: "number",
538
+ description: "Current step number (0-indexed).",
539
+ isOptional: false,
540
+ },
541
+ {
542
+ name: "finishReason",
543
+ type: "string",
544
+ description: "The finish reason from the LLM (stop, tool-use, length, etc.).",
545
+ isOptional: true,
546
+ },
547
+ {
548
+ name: "toolCalls",
549
+ type: "ToolCallInfo[]",
550
+ description: "Tool calls made in this step (if any).",
551
+ isOptional: true,
552
+ },
553
+ {
554
+ name: "text",
555
+ type: "string",
556
+ description: "Generated text from this step.",
557
+ isOptional: true,
558
+ },
559
+ {
560
+ name: "systemMessages",
561
+ type: "CoreMessage[]",
562
+ description: "All system messages for read/modify access.",
563
+ isOptional: true,
564
+ },
565
+ {
566
+ name: "abort",
567
+ type: "(reason?: string, options?: { retry?: boolean; metadata?: unknown }) => never",
568
+ description: "Function to abort processing. Pass `retry: true` to request the LLM retry the step.",
569
+ isOptional: false,
570
+ },
571
+ {
572
+ name: "retryCount",
573
+ type: "number",
574
+ description: "Number of times processors have triggered retry. Use this to limit retry attempts.",
575
+ isOptional: true,
576
+ },
577
+ {
578
+ name: "tracingContext",
579
+ type: "TracingContext",
580
+ description: "Tracing context for observability.",
581
+ isOptional: true,
582
+ },
583
+ {
584
+ name: "requestContext",
585
+ type: "RequestContext",
586
+ description: "Request-scoped context with execution metadata.",
587
+ isOptional: true,
588
+ },
589
+ ]}
590
+ />
591
+
592
+ #### Use cases
593
+
594
+ - Implementing quality guardrails that can request retries
595
+ - Validating LLM output before tool execution
596
+ - Adding per-step logging or metrics
597
+ - Implementing output moderation with retry capability
598
+
599
+ #### Example: Quality guardrail with retry
600
+
601
+ ```typescript title="src/mastra/processors/quality-guardrail.ts" showLineNumbers copy
602
+ import type { Processor } from "@mastra/core";
603
+
604
+ export class QualityGuardrail implements Processor {
605
+ id = "quality-guardrail";
606
+
607
+ async processOutputStep({ text, abort, retryCount }) {
608
+ const score = await evaluateResponseQuality(text);
609
+
610
+ if (score < 0.7) {
611
+ if (retryCount < 3) {
612
+ // Request retry with feedback for the LLM
613
+ abort("Response quality too low. Please provide more detail.", {
614
+ retry: true,
615
+ metadata: { qualityScore: score },
616
+ });
617
+ } else {
618
+ // Max retries reached, block the response
619
+ abort("Response quality too low after multiple attempts.");
620
+ }
621
+ }
622
+
623
+ return [];
624
+ }
625
+ }
626
+ ```
627
+
361
628
  ## Processor types
362
629
 
363
630
  Mastra provides type aliases to ensure processors implement the required methods:
@@ -369,9 +636,10 @@ type InputProcessor = Processor & (
369
636
  | { processInputStep: required }
370
637
  );
371
638
 
372
- // Must implement processOutputStream OR processOutputResult (or both)
639
+ // Must implement processOutputStream, processOutputStep, OR processOutputResult (or any combination)
373
640
  type OutputProcessor = Processor & (
374
641
  | { processOutputStream: required }
642
+ | { processOutputStep: required }
375
643
  | { processOutputResult: required }
376
644
  );
377
645
  ```
@@ -404,13 +672,46 @@ export class LowercaseProcessor implements Processor {
404
672
 
405
673
  ### Per-step processor with processInputStep
406
674
 
675
+ ```typescript title="src/mastra/processors/dynamic-model.ts" showLineNumbers copy
676
+ import type { Processor, ProcessInputStepArgs, ProcessInputStepResult } from "@mastra/core";
677
+
678
+ export class DynamicModelProcessor implements Processor {
679
+ id = "dynamic-model";
680
+
681
+ async processInputStep({
682
+ stepNumber,
683
+ steps,
684
+ toolChoice,
685
+ }: ProcessInputStepArgs): Promise<ProcessInputStepResult> {
686
+ // Use a fast model for initial response
687
+ if (stepNumber === 0) {
688
+ return { model: "openai/gpt-4o-mini" };
689
+ }
690
+
691
+ // Switch to powerful model after tool calls
692
+ if (steps.length > 0 && steps[steps.length - 1].toolCalls?.length) {
693
+ return { model: "openai/gpt-4o" };
694
+ }
695
+
696
+ // Disable tools after 5 steps to force completion
697
+ if (stepNumber > 5) {
698
+ return { toolChoice: "none" };
699
+ }
700
+
701
+ return {};
702
+ }
703
+ }
704
+ ```
705
+
706
+ ### Message transformer with processInputStep
707
+
407
708
  ```typescript title="src/mastra/processors/reasoning-transformer.ts" showLineNumbers copy
408
709
  import type { Processor, MastraDBMessage } from "@mastra/core";
409
710
 
410
711
  export class ReasoningTransformer implements Processor {
411
712
  id = "reasoning-transformer";
412
713
 
413
- async processInputStep({ messages, messageList, stepNumber }) {
714
+ async processInputStep({ messages, messageList }) {
414
715
  // Transform reasoning parts to thinking parts at each step
415
716
  // This is useful when switching between model providers
416
717
  for (const msg of messages) {
@@ -1255,7 +1255,7 @@ Contains monitoring and observability data from agent execution. Can include wor
1255
1255
 
1256
1256
  ### tripwire
1257
1257
 
1258
- Emitted when the stream is forcibly terminated due to content being blocked by output processors. This acts as a safety mechanism to prevent harmful or inappropriate content from being streamed.
1258
+ Emitted when the stream is forcibly terminated due to content being blocked by a processor. This acts as a safety mechanism to prevent harmful or inappropriate content from being streamed. The payload includes information about why the content was blocked and whether a retry was requested.
1259
1259
 
1260
1260
  <PropertiesTable
1261
1261
  content={[
@@ -1274,11 +1274,32 @@ Emitted when the stream is forcibly terminated due to content being blocked by o
1274
1274
  type: "TripwirePayload",
1275
1275
  parameters: [
1276
1276
  {
1277
- name: "tripwireReason",
1277
+ name: "reason",
1278
1278
  type: "string",
1279
1279
  description:
1280
1280
  "Explanation of why the content was blocked (e.g., 'Output processor blocked content')",
1281
1281
  },
1282
+ {
1283
+ name: "retry",
1284
+ type: "boolean",
1285
+ isOptional: true,
1286
+ description:
1287
+ "Whether the processor requested a retry of the step",
1288
+ },
1289
+ {
1290
+ name: "metadata",
1291
+ type: "unknown",
1292
+ isOptional: true,
1293
+ description:
1294
+ "Additional metadata from the processor (e.g., scores, categories)",
1295
+ },
1296
+ {
1297
+ name: "processorId",
1298
+ type: "string",
1299
+ isOptional: true,
1300
+ description:
1301
+ "ID of the processor that triggered the tripwire",
1302
+ },
1282
1303
  ],
1283
1304
  },
1284
1305
  ],
@@ -12,13 +12,7 @@ The `.stream()` method enables real-time streaming of responses from an agent wi
12
12
  ## Usage example
13
13
 
14
14
  ```ts title="index.ts" copy
15
- // Default Mastra format
16
- const mastraStream = await agent.stream("message for agent");
17
-
18
- // AI SDK v5 compatible format
19
- const aiSdkStream = await agent.stream("message for agent", {
20
- format: "aisdk",
21
- });
15
+ const stream = await agent.stream("message for agent");
22
16
  ```
23
17
 
24
18
  :::info
@@ -50,14 +44,6 @@ const aiSdkStream = await agent.stream("message for agent", {
50
44
 
51
45
  <PropertiesTable
52
46
  content={[
53
- {
54
- name: "format",
55
- type: "'mastra' | 'aisdk'",
56
- isOptional: true,
57
- defaultValue: "'mastra'",
58
- description:
59
- "Determines the output stream format. Use 'mastra' for Mastra's native format (default) or 'aisdk' for AI SDK v5 compatibility.",
60
- },
61
47
  {
62
48
  name: "maxSteps",
63
49
  type: "number",
@@ -650,9 +636,9 @@ const aiSdkStream = await agent.stream("message for agent", {
650
636
  content={[
651
637
  {
652
638
  name: "stream",
653
- type: "MastraModelOutput<Output> | AISDKV5OutputStream<Output>",
639
+ type: "MastraModelOutput<Output>",
654
640
  description:
655
- "Returns a streaming interface based on the format parameter. When format is 'mastra' (default), returns MastraModelOutput. When format is 'aisdk', returns AISDKV5OutputStream for AI SDK v5 compatibility.",
641
+ "Returns a MastraModelOutput instance that provides access to the streaming output.",
656
642
  },
657
643
  {
658
644
  name: "traceId",
@@ -683,32 +669,34 @@ for await (const chunk of stream.textStream) {
683
669
  console.log(chunk);
684
670
  }
685
671
 
672
+ // or access full stream
673
+ for await (const chunk of stream.fullStream) {
674
+ console.log(chunk);
675
+ }
676
+
686
677
  // Get full text after streaming
687
678
  const fullText = await stream.text;
688
679
  ```
689
680
 
690
681
  ### AI SDK v5 Format
691
682
 
683
+ To use the stream with AI SDK v5, you can convert it using our utility function `toAISdkStream`.
684
+
692
685
  ```ts title="index.ts" showLineNumbers copy
693
- import { stepCountIs } from "ai-v5";
686
+ import { stepCountIs, createUIMessageStreamResponse } from "ai";
687
+ import { toAISdkStream } from "@mastra/ai-sdk";
694
688
 
695
689
  const stream = await agent.stream("Tell me a story", {
696
- format: "aisdk",
697
690
  stopWhen: stepCountIs(3), // Stop after 3 steps
698
691
  modelSettings: {
699
692
  temperature: 0.7,
700
693
  },
701
694
  });
702
695
 
703
- // Use with AI SDK v5 compatible interfaces
704
- for await (const part of stream.fullStream) {
705
- if (part.type === "text-delta") {
706
- console.log(part.text);
707
- }
708
- }
709
-
710
696
  // In an API route for frontend integration
711
- return stream.toUIMessageStreamResponse();
697
+ return createUIMessageStreamResponse({
698
+ stream: toAISdkStream(stream, { from: "agent" }),
699
+ })
712
700
  ```
713
701
 
714
702
  ### Using Callbacks
@@ -744,10 +732,9 @@ for await (const chunk of stream.textStream) {
744
732
 
745
733
  ```ts title="index.ts" showLineNumbers copy
746
734
  import { z } from "zod";
747
- import { stepCountIs } from "ai-v5";
735
+ import { stepCountIs } from "ai";
748
736
 
749
737
  await agent.stream("message for agent", {
750
- format: "aisdk", // Enable AI SDK v5 compatibility
751
738
  stopWhen: stepCountIs(3), // Stop after 3 steps
752
739
  modelSettings: {
753
740
  temperature: 0.7,
@@ -5,7 +5,7 @@ description: Documentation for the `Workflow.foreach()` method in workflows, whi
5
5
 
6
6
  # Workflow.foreach()
7
7
 
8
- The `.foreach()` method creates a loop that executes a step for each item in an array.
8
+ The `.foreach()` method creates a loop that executes a step for each item in an array. It always returns an array containing the output from each iteration, preserving the original order.
9
9
 
10
10
  ## Usage example
11
11
 
@@ -50,11 +50,76 @@ workflow.foreach(step1, { concurrency: 2 });
50
50
  {
51
51
  name: "workflow",
52
52
  type: "Workflow",
53
- description: "The workflow instance for method chaining",
53
+ description: "The workflow instance for method chaining. The output type is an array of the step's output type.",
54
54
  },
55
55
  ]}
56
56
  />
57
57
 
58
+ ## Behavior
59
+
60
+ ### Execution and waiting
61
+
62
+ The `.foreach()` method processes all items before the next step executes. The step following `.foreach()` only runs after every iteration has completed, regardless of concurrency settings. With `concurrency: 1` (default), items process sequentially. With higher concurrency, items process in parallel batches, but the next step still waits for all batches to finish.
63
+
64
+ If you need to run multiple operations per item, use a nested workflow as the step. This keeps all operations for each item together and is cleaner than chaining multiple `.foreach()` calls. See [Nested workflows inside foreach](/docs/v1/workflows/control-flow#nested-workflows-inside-foreach) for examples.
65
+
66
+ ### Output structure
67
+
68
+ `.foreach()` always outputs an array. Each element in the output array corresponds to the result of processing the element at the same index in the input array.
69
+
70
+ ```typescript copy
71
+ // Input: [{ value: 1 }, { value: 2 }, { value: 3 }]
72
+ // Step adds 10 to each value
73
+ // Output: [{ value: 11 }, { value: 12 }, { value: 13 }]
74
+ ```
75
+
76
+ ### Using `.then()` after `.foreach()`
77
+
78
+ When you chain `.then()` after `.foreach()`, the next step receives the entire output array as its input. This allows you to aggregate or process all results together.
79
+
80
+ ```typescript copy
81
+ workflow
82
+ .foreach(processItemStep) // Output: array of processed items
83
+ .then(aggregateStep) // Input: the entire array
84
+ .commit();
85
+ ```
86
+
87
+ ### Using `.map()` after `.foreach()`
88
+
89
+ Use `.map()` to transform the array output before passing it to the next step:
90
+
91
+ ```typescript copy
92
+ workflow
93
+ .foreach(processItemStep)
94
+ .map(async ({ inputData }) => ({
95
+ total: inputData.reduce((sum, item) => sum + item.value, 0),
96
+ count: inputData.length
97
+ }))
98
+ .then(nextStep)
99
+ .commit();
100
+ ```
101
+
102
+ ### Chaining multiple `.foreach()` calls
103
+
104
+ When you chain `.foreach()` calls, each operates on the array from the previous step:
105
+
106
+ ```typescript copy
107
+ workflow
108
+ .foreach(stepA) // If input is [a, b, c], output is [A, B, C]
109
+ .foreach(stepB) // Operates on [A, B, C], output is [A', B', C']
110
+ .commit();
111
+ ```
112
+
113
+ If a step inside `.foreach()` returns an array, the output becomes an array of arrays. Use `.map()` with `.flat()` to flatten:
114
+
115
+ ```typescript copy
116
+ workflow
117
+ .foreach(chunkStep) // Output: [[chunk1, chunk2], [chunk3, chunk4]]
118
+ .map(async ({ inputData }) => inputData.flat()) // Output: [chunk1, chunk2, chunk3, chunk4]
119
+ .foreach(embedStep)
120
+ .commit();
121
+ ```
122
+
58
123
  ## Related
59
124
 
60
- - [Repeating with foreach](/docs/v1/workflows/control-flow#looping-with-foreach)
125
+ - [Looping with foreach](/docs/v1/workflows/control-flow#looping-with-foreach)
@@ -130,9 +130,32 @@ A workflow's `status` indicates its current execution state. The possible values
130
130
  description:
131
131
  "Workflow execution is paused waiting for resume, with suspended step information",
132
132
  },
133
+ {
134
+ name: "tripwire",
135
+ type: "string",
136
+ description:
137
+ "Workflow was terminated by a processor tripwire. This occurs when an agent step within the workflow triggers a tripwire (e.g., content was blocked by a guardrail). The tripwire information is available on the result.",
138
+ },
133
139
  ]}
134
140
  />
135
141
 
142
+ ### Handling tripwire status
143
+
144
+ When a workflow contains an agent step that triggers a tripwire, the workflow returns with `status: 'tripwire'` and includes tripwire details:
145
+
146
+ ```typescript showLineNumbers copy
147
+ const run = await workflow.createRun();
148
+ const result = await run.start({ inputData: { message: "Hello" } });
149
+
150
+ if (result.status === "tripwire") {
151
+ console.log("Workflow terminated by tripwire:", result.tripwire?.reason);
152
+ console.log("Processor ID:", result.tripwire?.processorId);
153
+ console.log("Retry requested:", result.tripwire?.retry);
154
+ }
155
+ ```
156
+
157
+ This is distinct from `status: 'failed'` which indicates an unexpected error. A tripwire status means a processor intentionally stopped execution (e.g., for content moderation).
158
+
136
159
  ## Related
137
160
 
138
161
  - [Step Class](./step)