@mastra/core 1.15.0-alpha.0 → 1.15.0-alpha.2

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 (161) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/dist/agent/index.cjs +8 -8
  3. package/dist/agent/index.js +1 -1
  4. package/dist/{chunk-JBYJDZT5.js → chunk-4YKZNIK6.js} +4 -4
  5. package/dist/{chunk-JBYJDZT5.js.map → chunk-4YKZNIK6.js.map} +1 -1
  6. package/dist/{chunk-I5ON7TPA.cjs → chunk-7BM5LQHF.cjs} +82 -82
  7. package/dist/{chunk-I5ON7TPA.cjs.map → chunk-7BM5LQHF.cjs.map} +1 -1
  8. package/dist/{chunk-IST54Q37.js → chunk-ASVFCNLS.js} +7 -7
  9. package/dist/{chunk-IST54Q37.js.map → chunk-ASVFCNLS.js.map} +1 -1
  10. package/dist/{chunk-DGSXFZGZ.js → chunk-CTJLKJMO.js} +10 -6
  11. package/dist/chunk-CTJLKJMO.js.map +1 -0
  12. package/dist/{chunk-4JBWS3Y6.js → chunk-EAWVRIHS.js} +3 -3
  13. package/dist/{chunk-4JBWS3Y6.js.map → chunk-EAWVRIHS.js.map} +1 -1
  14. package/dist/{chunk-7GXY5CDK.js → chunk-ECAVJWAH.js} +4 -4
  15. package/dist/{chunk-7GXY5CDK.js.map → chunk-ECAVJWAH.js.map} +1 -1
  16. package/dist/{chunk-EG3QZTQQ.cjs → chunk-EFYYAFWD.cjs} +19 -15
  17. package/dist/chunk-EFYYAFWD.cjs.map +1 -0
  18. package/dist/{chunk-ORYC6WMY.js → chunk-EYLPPZIF.js} +4 -2
  19. package/dist/chunk-EYLPPZIF.js.map +1 -0
  20. package/dist/{chunk-FIOAZZAM.js → chunk-FNOAF2SZ.js} +3 -3
  21. package/dist/{chunk-FIOAZZAM.js.map → chunk-FNOAF2SZ.js.map} +1 -1
  22. package/dist/{chunk-GMUEV4ML.cjs → chunk-FYQXIWRH.cjs} +4 -2
  23. package/dist/chunk-FYQXIWRH.cjs.map +1 -0
  24. package/dist/{chunk-KZLBDSIF.js → chunk-HIZDAENF.js} +3 -3
  25. package/dist/{chunk-KZLBDSIF.js.map → chunk-HIZDAENF.js.map} +1 -1
  26. package/dist/{chunk-7XPMIQIK.js → chunk-JIBMK2QP.js} +8 -8
  27. package/dist/{chunk-7XPMIQIK.js.map → chunk-JIBMK2QP.js.map} +1 -1
  28. package/dist/{chunk-CBLM3UY3.js → chunk-KCZ3R5SF.js} +3 -3
  29. package/dist/{chunk-CBLM3UY3.js.map → chunk-KCZ3R5SF.js.map} +1 -1
  30. package/dist/{chunk-Y2I3C7FR.cjs → chunk-M5BDH7B4.cjs} +6 -6
  31. package/dist/{chunk-Y2I3C7FR.cjs.map → chunk-M5BDH7B4.cjs.map} +1 -1
  32. package/dist/{chunk-4CC2ZV3B.js → chunk-MHTWFVXK.js} +3 -3
  33. package/dist/{chunk-4CC2ZV3B.js.map → chunk-MHTWFVXK.js.map} +1 -1
  34. package/dist/{chunk-REVBDBHI.cjs → chunk-NWPRZZ2K.cjs} +48 -48
  35. package/dist/{chunk-REVBDBHI.cjs.map → chunk-NWPRZZ2K.cjs.map} +1 -1
  36. package/dist/{chunk-PEKFBFE2.cjs → chunk-ORHPD25N.cjs} +7 -7
  37. package/dist/{chunk-PEKFBFE2.cjs.map → chunk-ORHPD25N.cjs.map} +1 -1
  38. package/dist/{chunk-SV6VG3XO.cjs → chunk-OX63O3QG.cjs} +5 -5
  39. package/dist/{chunk-SV6VG3XO.cjs.map → chunk-OX63O3QG.cjs.map} +1 -1
  40. package/dist/{chunk-NSJS72DA.cjs → chunk-Q64Z437G.cjs} +15 -15
  41. package/dist/{chunk-NSJS72DA.cjs.map → chunk-Q64Z437G.cjs.map} +1 -1
  42. package/dist/{chunk-W4R4TA4Z.cjs → chunk-RFZB2PQE.cjs} +9 -9
  43. package/dist/{chunk-W4R4TA4Z.cjs.map → chunk-RFZB2PQE.cjs.map} +1 -1
  44. package/dist/{chunk-J7UJLVIQ.cjs → chunk-TJB7IK7N.cjs} +19 -11
  45. package/dist/chunk-TJB7IK7N.cjs.map +1 -0
  46. package/dist/{chunk-OT7UVM2Z.cjs → chunk-X4RVX77L.cjs} +185 -185
  47. package/dist/{chunk-OT7UVM2Z.cjs.map → chunk-X4RVX77L.cjs.map} +1 -1
  48. package/dist/{chunk-XVOLOB5X.cjs → chunk-YV5UMIRV.cjs} +3 -3
  49. package/dist/{chunk-XVOLOB5X.cjs.map → chunk-YV5UMIRV.cjs.map} +1 -1
  50. package/dist/{chunk-SCTBRRU3.js → chunk-Z76WT6W3.js} +18 -10
  51. package/dist/chunk-Z76WT6W3.js.map +1 -0
  52. package/dist/datasets/index.cjs +11 -11
  53. package/dist/datasets/index.js +1 -1
  54. package/dist/docs/SKILL.md +10 -11
  55. package/dist/docs/assets/SOURCE_MAP.json +154 -154
  56. package/dist/docs/references/docs-agents-agent-approval.md +114 -193
  57. package/dist/docs/references/docs-agents-guardrails.md +120 -167
  58. package/dist/docs/references/docs-agents-networks.md +88 -205
  59. package/dist/docs/references/docs-agents-overview.md +47 -256
  60. package/dist/docs/references/docs-agents-processors.md +201 -297
  61. package/dist/docs/references/docs-agents-structured-output.md +13 -22
  62. package/dist/docs/references/docs-agents-supervisor-agents.md +24 -18
  63. package/dist/docs/references/docs-agents-using-tools.md +81 -104
  64. package/dist/docs/references/docs-memory-observational-memory.md +4 -2
  65. package/dist/docs/references/docs-memory-overview.md +219 -24
  66. package/dist/docs/references/docs-memory-semantic-recall.md +1 -1
  67. package/dist/docs/references/docs-memory-storage.md +4 -4
  68. package/dist/docs/references/docs-memory-working-memory.md +1 -1
  69. package/dist/docs/references/docs-observability-overview.md +1 -1
  70. package/dist/docs/references/docs-observability-tracing-exporters-arize.md +1 -1
  71. package/dist/docs/references/docs-server-request-context.md +1 -1
  72. package/dist/docs/references/docs-workflows-overview.md +1 -1
  73. package/dist/docs/references/docs-workspace-overview.md +1 -1
  74. package/dist/docs/references/guides-concepts-multi-agent-systems.md +75 -0
  75. package/dist/docs/references/reference-agents-agent.md +6 -8
  76. package/dist/docs/references/reference-agents-generate.md +74 -23
  77. package/dist/docs/references/reference-agents-getMemory.md +1 -1
  78. package/dist/docs/references/reference-agents-network.md +2 -2
  79. package/dist/docs/references/reference-ai-sdk-network-route.md +1 -1
  80. package/dist/docs/references/reference-ai-sdk-with-mastra.md +1 -1
  81. package/dist/docs/references/reference-core-getMemory.md +1 -2
  82. package/dist/docs/references/reference-core-listMemory.md +1 -2
  83. package/dist/docs/references/reference-harness-harness-class.md +2 -2
  84. package/dist/docs/references/reference-memory-observational-memory.md +3 -1
  85. package/dist/docs/references/reference-processors-processor-interface.md +2 -0
  86. package/dist/docs/references/reference-storage-overview.md +1 -1
  87. package/dist/docs/references/reference-templates-overview.md +1 -1
  88. package/dist/docs/references/reference-tools-create-tool.md +16 -4
  89. package/dist/evals/index.cjs +5 -5
  90. package/dist/evals/index.js +2 -2
  91. package/dist/evals/scoreTraces/index.cjs +3 -3
  92. package/dist/evals/scoreTraces/index.js +1 -1
  93. package/dist/harness/harness.d.ts +3 -0
  94. package/dist/harness/harness.d.ts.map +1 -1
  95. package/dist/harness/index.cjs +48 -13
  96. package/dist/harness/index.cjs.map +1 -1
  97. package/dist/harness/index.js +46 -11
  98. package/dist/harness/index.js.map +1 -1
  99. package/dist/harness/types.d.ts +11 -0
  100. package/dist/harness/types.d.ts.map +1 -1
  101. package/dist/index.cjs +2 -2
  102. package/dist/index.js +1 -1
  103. package/dist/llm/index.cjs +16 -16
  104. package/dist/llm/index.js +5 -5
  105. package/dist/llm/model/model.d.ts.map +1 -1
  106. package/dist/llm/model/provider-types.generated.d.ts +6 -2
  107. package/dist/loop/index.cjs +14 -14
  108. package/dist/loop/index.js +1 -1
  109. package/dist/mastra/index.cjs +2 -2
  110. package/dist/mastra/index.js +1 -1
  111. package/dist/memory/index.cjs +14 -14
  112. package/dist/memory/index.js +1 -1
  113. package/dist/memory/types.d.ts +7 -0
  114. package/dist/memory/types.d.ts.map +1 -1
  115. package/dist/models-dev-E6FRPGHV.js +3 -0
  116. package/dist/{models-dev-5BT32JYX.js.map → models-dev-E6FRPGHV.js.map} +1 -1
  117. package/dist/models-dev-WIROJ2IM.cjs +12 -0
  118. package/dist/{models-dev-OTMJW4WK.cjs.map → models-dev-WIROJ2IM.cjs.map} +1 -1
  119. package/dist/netlify-7IRBQ2BY.cjs +12 -0
  120. package/dist/{netlify-DT2P2NQD.cjs.map → netlify-7IRBQ2BY.cjs.map} +1 -1
  121. package/dist/netlify-OAGRP6WY.js +3 -0
  122. package/dist/{netlify-FJCQU3OY.js.map → netlify-OAGRP6WY.js.map} +1 -1
  123. package/dist/processor-provider/index.cjs +10 -10
  124. package/dist/processor-provider/index.js +1 -1
  125. package/dist/processors/index.cjs +42 -42
  126. package/dist/processors/index.js +1 -1
  127. package/dist/provider-registry-2MHU2NP6.js +3 -0
  128. package/dist/{provider-registry-BCSAL2IQ.js.map → provider-registry-2MHU2NP6.js.map} +1 -1
  129. package/dist/provider-registry-FINEGQHE.cjs +40 -0
  130. package/dist/{provider-registry-65WCBR2K.cjs.map → provider-registry-FINEGQHE.cjs.map} +1 -1
  131. package/dist/provider-registry.json +14 -6
  132. package/dist/relevance/index.cjs +3 -3
  133. package/dist/relevance/index.js +1 -1
  134. package/dist/stream/index.cjs +8 -8
  135. package/dist/stream/index.js +1 -1
  136. package/dist/test-utils/llm-mock.cjs +4 -4
  137. package/dist/test-utils/llm-mock.js +1 -1
  138. package/dist/tool-loop-agent/index.cjs +4 -4
  139. package/dist/tool-loop-agent/index.js +1 -1
  140. package/dist/workflows/default.d.ts +2 -2
  141. package/dist/workflows/default.d.ts.map +1 -1
  142. package/dist/workflows/evented/index.cjs +10 -10
  143. package/dist/workflows/evented/index.js +1 -1
  144. package/dist/workflows/index.cjs +24 -24
  145. package/dist/workflows/index.js +1 -1
  146. package/package.json +7 -7
  147. package/src/llm/model/provider-types.generated.d.ts +6 -2
  148. package/dist/chunk-DGSXFZGZ.js.map +0 -1
  149. package/dist/chunk-EG3QZTQQ.cjs.map +0 -1
  150. package/dist/chunk-GMUEV4ML.cjs.map +0 -1
  151. package/dist/chunk-J7UJLVIQ.cjs.map +0 -1
  152. package/dist/chunk-ORYC6WMY.js.map +0 -1
  153. package/dist/chunk-SCTBRRU3.js.map +0 -1
  154. package/dist/docs/references/docs-agents-agent-memory.md +0 -209
  155. package/dist/docs/references/docs-agents-network-approval.md +0 -278
  156. package/dist/models-dev-5BT32JYX.js +0 -3
  157. package/dist/models-dev-OTMJW4WK.cjs +0 -12
  158. package/dist/netlify-DT2P2NQD.cjs +0 -12
  159. package/dist/netlify-FJCQU3OY.js +0 -3
  160. package/dist/provider-registry-65WCBR2K.cjs +0 -40
  161. package/dist/provider-registry-BCSAL2IQ.js +0 -3
@@ -7,7 +7,7 @@ Processors are configured as:
7
7
  - **`inputProcessors`**: Run before messages reach the language model.
8
8
  - **`outputProcessors`**: Run after the language model generates a response, but before it's returned to users.
9
9
 
10
- You can use individual `Processor` objects or compose them into workflows using Mastra's workflow primitives. Workflows give you advanced control over processor execution order, parallel processing, and conditional logic.
10
+ You can use individual [`Processor`](https://mastra.ai/reference/processors/processor-interface) objects or compose them into workflows using Mastra's workflow primitives. Workflows give you advanced control over processor execution order, parallel processing, and conditional logic.
11
11
 
12
12
  Some processors implement both input and output logic and can be used in either array depending on where the transformation should occur.
13
13
 
@@ -26,7 +26,7 @@ Use processors to:
26
26
 
27
27
  Mastra includes several processors for common use cases. You can also create custom processors for application-specific requirements.
28
28
 
29
- ## Adding processors to an agent
29
+ ## Quickstart
30
30
 
31
31
  Import and instantiate the processor, then pass it to the agent's `inputProcessors` or `outputProcessors` array:
32
32
 
@@ -81,27 +81,20 @@ Your processors run first, then memory persists messages.
81
81
 
82
82
  This ordering ensures that if your output guardrail calls `abort()`, memory processors are skipped and no messages are saved. See [Memory Processors](https://mastra.ai/docs/memory/memory-processors) for details.
83
83
 
84
- ## Creating custom processors
84
+ ## Create custom processors
85
85
 
86
86
  Custom processors implement the `Processor` interface:
87
87
 
88
- ### Custom input processor
88
+ ### Transform input messages
89
89
 
90
90
  ```typescript
91
- import type { Processor, MastraDBMessage, RequestContext } from '@mastra/core'
91
+ import type { Processor, ProcessInputArgs } from '@mastra/core/processors'
92
+ import type { MastraDBMessage } from '@mastra/core/memory'
92
93
 
93
94
  export class CustomInputProcessor implements Processor {
94
95
  id = 'custom-input'
95
96
 
96
- async processInput({
97
- messages,
98
- systemMessages,
99
- context,
100
- }: {
101
- messages: MastraDBMessage[]
102
- systemMessages: CoreMessage[]
103
- context: RequestContext
104
- }): Promise<MastraDBMessage[]> {
97
+ async processInput({ messages }: ProcessInputArgs): Promise<MastraDBMessage[]> {
105
98
  // Transform messages before they reach the LLM
106
99
  return messages.map(msg => ({
107
100
  ...msg,
@@ -114,58 +107,20 @@ export class CustomInputProcessor implements Processor {
114
107
  }
115
108
  ```
116
109
 
117
- The `processInput` method receives:
110
+ The `processInput()` method receives `messages`, `systemMessages`, and an `abort()` function. Return a `MastraDBMessage[]` to replace messages, or `{ messages, systemMessages }` to also modify system messages.
118
111
 
119
- - `messages`: User and assistant messages (not system messages)
120
- - `systemMessages`: All system messages (agent instructions, memory context, user-provided system prompts)
121
- - `messageList`: The full MessageList instance for advanced use cases
122
- - `abort`: Function to stop processing and return early
123
- - `requestContext`: Execution metadata like `threadId` and `resourceId`
112
+ See the [`Processor` reference](https://mastra.ai/reference/processors/processor-interface) for all available arguments and return types.
124
113
 
125
- The method can return:
114
+ ### Control each step
126
115
 
127
- - `MastraDBMessage[]` Transformed messages array (backward compatible)
128
- - `{ messages: MastraDBMessage[]; systemMessages: CoreMessage[] }` — Both messages and modified system messages
129
-
130
- The framework handles both return formats, so modifying system messages is optional and existing processors continue to work.
131
-
132
- ### Modifying system messages
133
-
134
- To modify system messages (e.g., trim verbose prompts for smaller models), return an object with both `messages` and `systemMessages`:
116
+ While `processInput()` runs once at the start of agent execution, `processInputStep()` runs at **each step** of the agentic loop (including tool call continuations). This enables per-step configuration changes like dynamic model switching or tool choice modifications.
135
117
 
136
118
  ```typescript
137
- import type { Processor, CoreMessage, MastraDBMessage } from '@mastra/core'
138
-
139
- export class SystemTrimmer implements Processor {
140
- id = 'system-trimmer'
141
-
142
- async processInput({
143
- messages,
144
- systemMessages,
145
- }): Promise<{ messages: MastraDBMessage[]; systemMessages: CoreMessage[] }> {
146
- // Trim system messages for smaller models
147
- const trimmedSystemMessages = systemMessages.map(msg => ({
148
- ...msg,
149
- content: typeof msg.content === 'string' ? msg.content.substring(0, 500) : msg.content,
150
- }))
151
-
152
- return { messages, systemMessages: trimmedSystemMessages }
153
- }
154
- }
155
- ```
156
-
157
- This is useful for:
158
-
159
- - Trimming verbose system prompts for models with smaller context windows
160
- - Filtering or modifying semantic recall content to prevent "prompt too long" errors
161
- - Dynamically adjusting system instructions based on the conversation
162
-
163
- ### Per-step processing with `processInputStep`
164
-
165
- While `processInput` runs once at the start of agent execution, `processInputStep` runs at **each step** of the agentic loop (including tool call continuations). This enables per-step configuration changes like dynamic model switching or tool choice modifications.
166
-
167
- ```typescript
168
- import type { Processor, ProcessInputStepArgs, ProcessInputStepResult } from '@mastra/core'
119
+ import type {
120
+ Processor,
121
+ ProcessInputStepArgs,
122
+ ProcessInputStepResult,
123
+ } from '@mastra/core/processors'
169
124
 
170
125
  export class DynamicModelProcessor implements Processor {
171
126
  id = 'dynamic-model'
@@ -192,100 +147,13 @@ export class DynamicModelProcessor implements Processor {
192
147
  }
193
148
  ```
194
149
 
195
- The `processInputStep` method receives:
196
-
197
- - `stepNumber`: Current step in the agentic loop (0-indexed)
198
- - `steps`: Results from previous steps
199
- - `messages`: Current messages snapshot (read-only)
200
- - `systemMessages`: Current system messages (read-only)
201
- - `messageList`: The full MessageList instance for mutations
202
- - `model`: Current model being used
203
- - `tools`: Current tools available for this step
204
- - `toolChoice`: Current tool choice setting
205
- - `activeTools`: Currently active tools
206
- - `providerOptions`: Provider-specific options
207
- - `modelSettings`: Model settings like temperature
208
- - `structuredOutput`: Structured output configuration
209
-
210
- The method can return any combination of:
211
-
212
- - `model`: Change the model for this step
213
- - `tools`: Replace or add tools (use spread to merge: `{ tools: { ...tools, newTool } }`)
214
- - `toolChoice`: Change tool selection behavior
215
- - `activeTools`: Filter which tools are available
216
- - `messages`: Replace messages (applied to messageList)
217
- - `systemMessages`: Replace all system messages
218
- - `providerOptions`: Modify provider options
219
- - `modelSettings`: Modify model settings
220
- - `structuredOutput`: Modify structured output configuration
221
-
222
- #### Ensuring a final response with `maxSteps`
223
-
224
- When using `maxSteps` to limit agent execution, the agent may return an empty response if it attempts a tool call on the final step. Use `processInputStep` to force a text response on the last step:
225
-
226
- ```typescript
227
- import { Processor, ProcessInputStepArgs, ProcessInputStepResult } from '@mastra/core/processors'
150
+ The method receives the current `stepNumber`, `model`, `tools`, `toolChoice`, `messages`, and more. Return an object with any properties you want to override for that step, for example `{ model, toolChoice, tools, systemMessages }`.
228
151
 
229
- export class EnsureFinalResponseProcessor implements Processor {
230
- readonly id = 'ensure-final-response'
152
+ See the [`Processor` reference](https://mastra.ai/reference/processors/processor-interface) for all available arguments and return types.
231
153
 
232
- private maxSteps: number
154
+ ### Use the `prepareStep()` callback
233
155
 
234
- constructor(maxSteps: number) {
235
- this.maxSteps = maxSteps
236
- }
237
-
238
- async processInputStep({
239
- stepNumber,
240
- systemMessages,
241
- }: ProcessInputStepArgs): Promise<ProcessInputStepResult> {
242
- // On the last step, prevent tool calls and instruct the LLM to summarize
243
- if (stepNumber === this.maxSteps - 1) {
244
- return {
245
- tools: {},
246
- toolChoice: 'none',
247
- systemMessages: [
248
- ...systemMessages,
249
- {
250
- role: 'system',
251
- content:
252
- 'You have reached the maximum number of steps. Summarize your progress so far and provide a best-effort response. If the task is incomplete, clearly indicate what remains to be done.',
253
- },
254
- ],
255
- }
256
- }
257
- return {}
258
- }
259
- }
260
- ```
261
-
262
- Use it with your agent:
263
-
264
- ```typescript
265
- import { Agent } from '@mastra/core/agent'
266
- import { EnsureFinalResponseProcessor } from '../processors/ensure-final-response'
267
-
268
- const MAX_STEPS = 5
269
-
270
- const agent = new Agent({
271
- id: 'bounded-agent',
272
- name: 'Bounded Agent',
273
- model: 'openai/gpt-5-mini',
274
- tools: {
275
- /* your tools */
276
- },
277
- inputProcessors: [new EnsureFinalResponseProcessor(MAX_STEPS)],
278
- })
279
-
280
- // Pass maxSteps when calling generate/stream
281
- const result = await agent.generate('Your prompt', { maxSteps: MAX_STEPS })
282
- ```
283
-
284
- This ensures that on the final allowed step (step 4 when `maxSteps` is 5, since steps are 0-indexed), the LLM generates a summary instead of attempting another tool call, and clearly indicates if the task is incomplete.
285
-
286
- #### Using `prepareStep` callback
287
-
288
- For simpler per-step logic, you can use the `prepareStep` callback on `generate()` or `stream()` instead of creating a full processor:
156
+ The `prepareStep()` callback on `generate()` or `stream()` is a shorthand for `processInputStep()`. Internally, Mastra wraps it in a processor that calls your function at each step. It accepts the same arguments and return type as `processInputStep()`, but doesn't require creating a class:
289
157
 
290
158
  ```typescript
291
159
  await agent.generate('Complex task', {
@@ -300,10 +168,11 @@ await agent.generate('Complex task', {
300
168
  })
301
169
  ```
302
170
 
303
- ### Custom output processor
171
+ ### Transform output messages
304
172
 
305
173
  ```typescript
306
- import type { Processor, MastraDBMessage, ChunkType } from '@mastra/core'
174
+ import type { Processor } from '@mastra/core/processors'
175
+ import type { MastraDBMessage } from '@mastra/core/memory'
307
176
 
308
177
  export class CustomOutputProcessor implements Processor {
309
178
  id = 'custom-output'
@@ -312,6 +181,35 @@ export class CustomOutputProcessor implements Processor {
312
181
  // Transform messages after the LLM generates them
313
182
  return messages.filter(msg => msg.role !== 'system')
314
183
  }
184
+ }
185
+ ```
186
+
187
+ The method also receives a `result` object with the full generation data — `text`, `usage` (token counts), `finishReason`, and `steps` (each containing `toolCalls`, `toolResults`, etc.). Use it to track usage or inspect tool calls:
188
+
189
+ ```typescript
190
+ import type { Processor } from '@mastra/core/processors'
191
+
192
+ export class UsageTracker implements Processor {
193
+ id = 'usage-tracker'
194
+
195
+ async processOutputResult({ messages, result }) {
196
+ console.log(`Tokens: ${result.usage.inputTokens} in, ${result.usage.outputTokens} out`)
197
+ console.log(`Finish reason: ${result.finishReason}`)
198
+ return messages
199
+ }
200
+ }
201
+ ```
202
+
203
+ ### Filter streamed output
204
+
205
+ The `processOutputStream()` method transforms or filters streaming chunks before they reach the client:
206
+
207
+ ```typescript
208
+ import type { Processor } from '@mastra/core/processors'
209
+ import type { ChunkType } from '@mastra/core/stream'
210
+
211
+ export class StreamFilter implements Processor {
212
+ id = 'stream-filter'
315
213
 
316
214
  async processOutputStream({ part }): Promise<ChunkType | null> {
317
215
  // Transform or filter streaming chunks
@@ -320,42 +218,132 @@ export class CustomOutputProcessor implements Processor {
320
218
  }
321
219
  ```
322
220
 
323
- The `processOutputStream` method receives all streaming chunks. To also receive custom `data-*` chunks emitted by tools via `writer.custom()`, set `processDataParts = true` on your processor. This lets you inspect, modify, or block tool-emitted data chunks before they reach the client.
221
+ To also receive custom `data-*` chunks emitted by tools via `writer.custom()`, set `processDataParts = true` on your processor. This lets you inspect, modify, or block tool-emitted data chunks before they reach the client.
324
222
 
325
- #### Accessing generation result data
223
+ ### Validate each response
326
224
 
327
- The `processOutputResult` method receives a `result` object containing the resolved generation data the same information available in the `onFinish` callback. This lets you access token usage, generated text, finish reason, and step details.
225
+ The `processOutputStep()` method runs after each LLM step, allowing you to validate the response and optionally request a retry:
328
226
 
329
227
  ```typescript
330
- import type { Processor } from '@mastra/core'
228
+ import type { Processor } from '@mastra/core/processors'
331
229
 
332
- export class UsageTracker implements Processor {
333
- id = 'usage-tracker'
230
+ export class ResponseValidator implements Processor {
231
+ id = 'response-validator'
334
232
 
335
- async processOutputResult({ messages, result }) {
336
- console.log(`Text: ${result.text}`)
337
- console.log(`Tokens: ${result.usage.inputTokens} in, ${result.usage.outputTokens} out`)
338
- console.log(`Finish reason: ${result.finishReason}`)
339
- console.log(`Steps: ${result.steps.length}`)
233
+ async processOutputStep({ text, abort, retryCount }) {
234
+ const isValid = await validateResponse(text)
340
235
 
341
- // Each step contains toolCalls, toolResults, reasoning, sources, files, etc.
342
- for (const step of result.steps) {
343
- if (step.toolCalls?.length) {
344
- console.log(`Step used ${step.toolCalls.length} tool calls`)
345
- }
236
+ if (!isValid && retryCount < 3) {
237
+ abort('Response did not meet requirements. Try again.', { retry: true })
346
238
  }
347
239
 
348
- return messages
240
+ return []
349
241
  }
350
242
  }
351
243
  ```
352
244
 
353
- #### Emitting custom stream events with writer
245
+ For more on retry behavior, see [Retry mechanism](#retry-mechanism) in Advanced patterns.
246
+
247
+ ## Built-in utility processors
248
+
249
+ Mastra provides utility processors for common tasks:
250
+
251
+ **For security and validation processors**, see the [Guardrails](https://mastra.ai/docs/agents/guardrails) page for input/output guardrails and moderation processors. **For memory-specific processors**, see the [Memory Processors](https://mastra.ai/docs/memory/memory-processors) page for processors that handle message history, semantic recall, and working memory.
252
+
253
+ ### `TokenLimiter`
254
+
255
+ Prevents context window overflow by removing older messages when the total token count exceeds a specified limit. Prioritizes recent messages and preserves system messages.
256
+
257
+ ```typescript
258
+ import { Agent } from '@mastra/core/agent'
259
+ import { TokenLimiter } from '@mastra/core/processors'
260
+
261
+ const agent = new Agent({
262
+ name: 'my-agent',
263
+ model: 'openai/gpt-5.4',
264
+ inputProcessors: [new TokenLimiter(127000)],
265
+ })
266
+ ```
267
+
268
+ See the [`TokenLimiterProcessor` reference](https://mastra.ai/reference/processors/token-limiter-processor) for custom encoding, strategy, and count mode options.
269
+
270
+ ### `ToolCallFilter`
271
+
272
+ Removes tool calls and results from messages sent to the LLM, saving tokens on verbose tool interactions. Optionally exclude only specific tools. This filter only affects the LLM input, filtered messages are still saved to memory.
273
+
274
+ See the [`ToolCallFilter` reference](https://mastra.ai/reference/processors/tool-call-filter) for configuration options and the [Memory Processors](https://mastra.ai/docs/memory/memory-processors) page for pre-memory filtering.
275
+
276
+ ### `ToolSearchProcessor`
277
+
278
+ Enables dynamic tool discovery for agents with large tool libraries. Instead of providing all tools upfront, the processor gives the agent `search_tools` and `load_tool` meta-tools to find and load tools by keyword on demand, reducing context token usage.
279
+
280
+ See the [`ToolSearchProcessor` reference](https://mastra.ai/reference/processors/tool-search-processor) for configuration options and usage examples.
281
+
282
+ ## Advanced patterns
283
+
284
+ ### Ensure a final response with `maxSteps`
285
+
286
+ When using `maxSteps` to limit agent execution, the agent may return an empty response if it attempts a tool call on the final step. Use `processInputStep()` to force a text response on the last step:
287
+
288
+ ```typescript
289
+ import type {
290
+ Processor,
291
+ ProcessInputStepArgs,
292
+ ProcessInputStepResult,
293
+ } from '@mastra/core/processors'
294
+
295
+ export class EnsureFinalResponseProcessor implements Processor {
296
+ readonly id = 'ensure-final-response'
297
+
298
+ private maxSteps: number
299
+
300
+ constructor(maxSteps: number) {
301
+ this.maxSteps = maxSteps
302
+ }
303
+
304
+ async processInputStep({
305
+ stepNumber,
306
+ systemMessages,
307
+ }: ProcessInputStepArgs): Promise<ProcessInputStepResult> {
308
+ // On the last step, prevent tool calls and instruct the LLM to summarize
309
+ if (stepNumber === this.maxSteps - 1) {
310
+ return {
311
+ tools: {},
312
+ toolChoice: 'none',
313
+ systemMessages: [
314
+ ...systemMessages,
315
+ {
316
+ role: 'system',
317
+ content:
318
+ 'You have reached the maximum number of steps. Summarize your progress so far and provide a best-effort response. If the task is incomplete, clearly indicate what remains to be done.',
319
+ },
320
+ ],
321
+ }
322
+ }
323
+ return {}
324
+ }
325
+ }
326
+ ```
327
+
328
+ Add it to `inputProcessors` and pass the same `maxSteps` value to `generate()` or `stream()`:
329
+
330
+ ```typescript
331
+ const MAX_STEPS = 5
332
+
333
+ const agent = new Agent({
334
+ inputProcessors: [new EnsureFinalResponseProcessor(MAX_STEPS)],
335
+ // ...
336
+ })
337
+
338
+ await agent.generate('Your prompt', { maxSteps: MAX_STEPS })
339
+ ```
340
+
341
+ ### Emit custom stream events
354
342
 
355
343
  Output processors receive a `writer` object that lets you emit custom data chunks back to the client during streaming. This is useful for use cases like streaming moderation results or sending UI update signals without blocking the original stream.
356
344
 
357
345
  ```typescript
358
- import type { Processor, ChunkType, MastraDBMessage } from '@mastra/core'
346
+ import type { Processor } from '@mastra/core/processors'
359
347
 
360
348
  export class ModerationProcessor implements Processor {
361
349
  id = 'moderation'
@@ -402,12 +390,13 @@ for await (const chunk of stream.fullStream) {
402
390
 
403
391
  Custom chunk types must use the `data-` prefix (e.g., `data-moderation-update`, `data-status`).
404
392
 
405
- #### Adding metadata in output processors
393
+ ### Add metadata to messages
406
394
 
407
395
  You can add custom metadata to messages in `processOutputResult`. This metadata is accessible via the response object:
408
396
 
409
397
  ```typescript
410
- import type { Processor, MastraDBMessage } from '@mastra/core'
398
+ import type { Processor } from '@mastra/core/processors'
399
+ import type { MastraDBMessage } from '@mastra/core/memory'
411
400
 
412
401
  export class MetadataProcessor implements Processor {
413
402
  id = 'metadata-processor'
@@ -447,124 +436,20 @@ const assistantMessage = result.response?.uiMessages?.find(m => m.role === 'assi
447
436
  console.log(assistantMessage?.metadata?.customData)
448
437
  ```
449
438
 
450
- Access the metadata when streaming:
451
-
452
- ```typescript
453
- const stream = await agent.stream('Hello')
454
-
455
- for await (const chunk of stream.fullStream) {
456
- if (chunk.type === 'finish') {
457
- // Access response with processor-added metadata from the finish chunk
458
- const uiMessages = chunk.payload.response?.uiMessages
459
- const assistantMessage = uiMessages?.find(m => m.role === 'assistant')
460
- console.log(assistantMessage?.metadata?.customData)
461
- }
462
- }
463
-
464
- // Or via the response promise after consuming the stream
465
- const response = await stream.response
466
- console.log(response.uiMessages)
467
- ```
468
-
469
- ## Built-in utility processors
470
-
471
- Mastra provides utility processors for common tasks:
472
-
473
- **For security and validation processors**, see the [Guardrails](https://mastra.ai/docs/agents/guardrails) page for input/output guardrails and moderation processors. **For memory-specific processors**, see the [Memory Processors](https://mastra.ai/docs/memory/memory-processors) page for processors that handle message history, semantic recall, and working memory.
474
-
475
- ### `TokenLimiter`
476
-
477
- Prevents context window overflow by removing older messages when the total token count exceeds a specified limit.
478
-
479
- ```typescript
480
- import { Agent } from '@mastra/core/agent'
481
- import { TokenLimiter } from '@mastra/core/processors'
482
-
483
- const agent = new Agent({
484
- name: 'my-agent',
485
- model: 'openai/gpt-5.4',
486
- inputProcessors: [
487
- // Ensure the total tokens don't exceed ~127k
488
- new TokenLimiter(127000),
489
- ],
490
- })
491
- ```
492
-
493
- The `TokenLimiter` uses the `o200k_base` encoding by default. You can specify other encodings for different models:
494
-
495
- ```typescript
496
- import cl100k_base from 'js-tiktoken/ranks/cl100k_base'
497
-
498
- const agent = new Agent({
499
- name: 'my-agent',
500
- inputProcessors: [
501
- new TokenLimiter({
502
- limit: 16000, // Example limit for a 16k context model
503
- encoding: cl100k_base,
504
- }),
505
- ],
506
- })
507
- ```
508
-
509
- ### `ToolCallFilter`
510
-
511
- Removes tool calls from messages sent to the LLM, saving tokens by excluding potentially verbose tool interactions.
512
-
513
- ```typescript
514
- import { Agent } from '@mastra/core/agent'
515
- import { ToolCallFilter, TokenLimiter } from '@mastra/core/processors'
516
-
517
- const agent = new Agent({
518
- name: 'my-agent',
519
- model: 'openai/gpt-5.4',
520
- inputProcessors: [
521
- // Example 1: Remove all tool calls/results
522
- new ToolCallFilter(),
523
-
524
- // Example 2: Remove only specific tool calls
525
- new ToolCallFilter({ exclude: ['generateImageTool'] }),
526
-
527
- // Always place TokenLimiter last
528
- new TokenLimiter(127000),
529
- ],
530
- })
531
- ```
532
-
533
- > **Note:** The example above filters tool calls and limits tokens for the LLM, but these filtered messages will still be saved to memory. To also filter messages before they're saved to memory, manually add memory processors before utility processors. See [Memory Processors](https://mastra.ai/docs/memory/memory-processors) for details.
534
-
535
- ### `ToolSearchProcessor`
536
-
537
- Enables dynamic tool discovery and loading for agents with large tool libraries. Instead of providing all tools upfront, the agent searches for tools by keyword and loads them on demand, reducing context token usage.
538
-
539
- ```typescript
540
- import { Agent } from '@mastra/core/agent'
541
- import { ToolSearchProcessor } from '@mastra/core/processors'
542
-
543
- const agent = new Agent({
544
- name: 'my-agent',
545
- model: 'openai/gpt-5.4',
546
- inputProcessors: [
547
- new ToolSearchProcessor({
548
- tools: {
549
- createIssue: githubTools.createIssue,
550
- sendEmail: emailTools.send,
551
- // ... hundreds of tools
552
- },
553
- search: { topK: 5, minScore: 0.1 },
554
- }),
555
- ],
556
- })
557
- ```
558
-
559
- The processor gives the agent two meta-tools: `search_tools` to find tools by keyword and `load_tool` to add a tool to the conversation. Loaded tools persist within the thread. See the [ToolSearchProcessor reference](https://mastra.ai/reference/processors/tool-search-processor) for full configuration options.
439
+ For streaming, access metadata from the `finish` chunk payload or the `stream.response` promise.
560
440
 
561
- ## Using workflows as processors
441
+ ### Use workflows as processors
562
442
 
563
443
  You can use Mastra workflows as processors to create complex processing pipelines with parallel execution, conditional branching, and error handling:
564
444
 
565
445
  ```typescript
566
446
  import { createWorkflow, createStep } from '@mastra/core/workflows'
567
- import { ProcessorStepSchema } from '@mastra/core/processors'
447
+ import {
448
+ ProcessorStepSchema,
449
+ PromptInjectionDetector,
450
+ PIIDetector,
451
+ ModerationProcessor,
452
+ } from '@mastra/core/processors'
568
453
  import { Agent } from '@mastra/core/agent'
569
454
 
570
455
  // Create a workflow that runs multiple checks in parallel
@@ -573,11 +458,26 @@ const moderationWorkflow = createWorkflow({
573
458
  inputSchema: ProcessorStepSchema,
574
459
  outputSchema: ProcessorStepSchema,
575
460
  })
576
- .then(createStep(new LengthValidator({ maxLength: 10000 })))
577
461
  .parallel([
578
- createStep(new PIIDetector({ strategy: 'redact' })),
579
- createStep(new ToxicityChecker({ threshold: 0.8 })),
462
+ createStep(
463
+ new PIIDetector({
464
+ strategy: 'redact',
465
+ }),
466
+ ),
467
+ createStep(
468
+ new PromptInjectionDetector({
469
+ strategy: 'block',
470
+ }),
471
+ ),
472
+ createStep(
473
+ new ModerationProcessor({
474
+ strategy: 'block',
475
+ }),
476
+ ),
580
477
  ])
478
+ .map(async ({ inputData }) => {
479
+ return inputData['processor:pii-detector']
480
+ })
581
481
  .commit()
582
482
 
583
483
  // Use the workflow as an input processor
@@ -589,14 +489,18 @@ const agent = new Agent({
589
489
  })
590
490
  ```
591
491
 
492
+ After a `.parallel()` step, each branch result is keyed by its processor ID (e.g. `processor:pii-detector`). Use `.map()` to select the branch whose output the next step should receive.
493
+
494
+ If a branch uses a mutating strategy like `redact`, map to that branch so its transformed messages carry forward. If all branches only `block`, any branch works. Pick any one since none of them modify the messages.
495
+
592
496
  When an agent is registered with Mastra, processor workflows are automatically registered as workflows, allowing you to view and debug them in the [Studio](https://mastra.ai/docs/getting-started/studio).
593
497
 
594
- ## Retry mechanism
498
+ ### Retry mechanism
595
499
 
596
500
  Processors can request that the LLM retry its response with feedback. This is useful for implementing quality checks, output validation, or iterative refinement:
597
501
 
598
502
  ```typescript
599
- import type { Processor } from '@mastra/core'
503
+ import type { Processor } from '@mastra/core/processors'
600
504
 
601
505
  export class QualityChecker implements Processor {
602
506
  id = 'quality-checker'
@@ -627,14 +531,14 @@ const agent = new Agent({
627
531
 
628
532
  The retry mechanism:
629
533
 
630
- - Only works in `processOutputStep` and `processInputStep` methods
534
+ - Only works in `processOutputStep()` and `processInputStep()` methods
631
535
  - Replays the step with the abort reason added as context for the LLM
632
536
  - Tracks retry count via the `retryCount` parameter
633
537
  - Respects `maxProcessorRetries` limit on the agent
634
538
 
635
539
  ## Related documentation
636
540
 
637
- - [Guardrails](https://mastra.ai/docs/agents/guardrails) - Security and validation processors
638
- - [Memory Processors](https://mastra.ai/docs/memory/memory-processors) - Memory-specific processors and automatic integration
639
- - [Processor Interface](https://mastra.ai/reference/processors/processor-interface) - Full API reference for processors
640
- - [ToolSearchProcessor Reference](https://mastra.ai/reference/processors/tool-search-processor) - API reference for dynamic tool search
541
+ - [Guardrails](https://mastra.ai/docs/agents/guardrails): Security and validation processors
542
+ - [Memory Processors](https://mastra.ai/docs/memory/memory-processors): Memory-specific processors and automatic integration
543
+ - [Processor Interface](https://mastra.ai/reference/processors/processor-interface): Full API reference for processors
544
+ - [ToolSearchProcessor Reference](https://mastra.ai/reference/processors/tool-search-processor): API reference for dynamic tool search