-
Notifications
You must be signed in to change notification settings - Fork 1.3k
feat: enhanced processor system with retry, workflow orchestration, and tripwire improvements #10947
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
🦋 Changeset detectedLatest commit: 710d747 The changes in this PR will be included in the next version bump. This PR includes changesets to release 19 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
WalkthroughAdds workflow-as-processor support and replaces primitive tripwire flags with structured tripwire objects (reason, retry, metadata, processorId). Threads processor/workflow unions and maxProcessorRetries across agents, loops/LLM/streams, processor runner, SDKs, UI, examples, tests, and docs; introduces processor step schemas and per-step processing hooks. Changes
Estimated code review effort🎯 5 (Critical) | ⏱️ ~120 minutes Areas to focus during review:
Possibly related PRs
Suggested labels
Suggested reviewers
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
📜 Recent review detailsConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro 📒 Files selected for processing (2)
🚧 Files skipped from review as they are similar to previous changes (1)
🧰 Additional context used📓 Path-based instructions (3)**/*.{ts,tsx}📄 CodeRabbit inference engine (CLAUDE.md)
Files:
**/*.{ts,tsx,js,jsx,json,md}📄 CodeRabbit inference engine (CLAUDE.md)
Files:
packages/**/*.{ts,tsx}📄 CodeRabbit inference engine (CLAUDE.md)
Files:
🔇 Additional comments (4)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 10
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/core/src/workflows/types.ts (1)
203-212: Fix incomplete status handling for'tripwire'and other statuses inexecute()method andMastraWorkflowStream.The new
'tripwire'status addition reveals broader exhaustiveness issues:
workflow.ts execute() method (lines 1540-1544): Returns
undefinedfor'tripwire'status instead of properly handling it. The method only checks'failed'(throws) and'success'(returns result), leaving'tripwire','suspended','waiting','pending','canceled', and'bailed'statuses returningundefinedsilently.MastraWorkflowStream.ts (lines 106-120): Status assignment logic never sets status to
'tripwire'. The else-if chain handles'canceled','suspended', and'failed', but has no case for tripwire, so workflow-finish events emit incorrect status.handlers/control-flow.ts (lines 165, 432, 197, 459): Parallel and conditional handlers check for
'failed'but don't explicitly propagate'tripwire'status through execution paths, though tripwire property is preserved internally.All
WorkflowRunStatusvariants must be handled exhaustively in switch statements and conditional chains to prevent silent failures.
🧹 Nitpick comments (33)
packages/playground-ui/src/domains/workflows/components/workflow-information.tsx (1)
80-84: Use design-token-based classes instead of raw Tailwind colors for processor badgeThe processor badge behavior and conditional rendering look good, but
className="bg-violet-500/20 text-violet-400"couples this UI to raw Tailwind palette values. Please swap these for the appropriate design-token-derived utility classes fromsrc/ds/tokens/index.ts(or an existingBadgevariant) to stay aligned with the design system. As per coding guidelines, ...packages/playground-ui/src/domains/workflows/components/workflow-table/columns.tsx (1)
15-28: Processor badge UX is clear; consider minor consistency + token usage tweaksThe new
workflowalias and conditional Processor badge integration intoEntryCelllook good and match the PR’s processor-workflow goals.Two small nits you may want to consider:
- For consistency, you could use
workflowinstead ofrow.originalin the Link (paths.workflowLink(workflow.id)andworkflow.name).- The badge uses
bg-violet-500/20 text-violet-400andh-3 w-3. If the design system exposes equivalent token-based classes or avariant/toneAPI onBadge, prefer those over hardcoded Tailwind color utilities to stay aligned with the playground UI token guidelines.packages/playground-ui/src/domains/workflows/utils.ts (1)
14-14: Clarify the comment to match actual logic.The comment states "failed step with tripwire property," but the code doesn't verify that
result.status === 'failed'. Any step with a truthytripwireproperty will trigger this handling, regardless of status.Apply this diff to clarify the comment:
-// Check if this is a tripwire (failed step with tripwire property) +// Check if this step has a tripwire (tripwire takes precedence over error)packages/playground-ui/src/components/ui/autoform/zodProvider/default-values.ts (1)
6-10: Literal default handling looks correct; consider simplifying thevaluesaccessThe new
$ZodLiteralbranch correctly derives a default fromschema._zod.def.values, which in Zod v4 is an array of allowed literal values, so taking the first one is a reasonable convention for autoform defaults. Given the type is always an array, theArray.isArrayguard is redundant and you could simplify this to something like:const [first] = schema._zod.def.values; return first;or add a small nullish fallback if you want to guard against an unexpected empty
valuesarray.This is a minor readability tweak; behavior is otherwise sound.
packages/core/src/processors/examples/processor-features-demo.example.ts (1)
1-351: Consider making these examples executable or moving to documentation.The entire file consists of commented-out code. While the examples are comprehensive and well-structured for demonstrating the new processor features, commented-out code in the codebase has limited utility:
- It won't be type-checked, so it may become stale as APIs evolve
- Users cannot import and run these examples
- It adds to code maintenance burden
Consider one of these alternatives:
- Uncomment the code to make it a proper runnable example file
- Convert to integration tests that verify the examples work correctly
- Move to documentation (e.g., a markdown file in docs with code snippets)
workflows/inngest/src/execution-engine.ts (1)
363-381: Add explicit type guard for the success status to eliminate theanycast.The
WorkflowResulttype already defines aresultproperty on the success branch, so the(result as any)?.resultcast is unnecessary. After theif (result.status === 'suspended')andelse if (result.status === 'tripwire')checks, add an explicitif (result.status === 'success')guard before lines 364-381 to enable proper TypeScript type narrowing and safely accessresult.resultwithout casting toany.packages/core/src/workflows/types.ts (1)
468-491: Consider tightening themetadatatype.The tripwire variant structure looks good, but the
metadatafield is typed asunknown, which is very permissive. Consider using a more specific type (e.g.,Record<string, unknown>or a dedicatedTripWireMetadatainterface) to provide better type safety and IDE support for consumers./** Metadata from the tripwire */ - metadata?: unknown; + metadata?: Record<string, unknown>;packages/core/src/processors/processors/structured-output.ts (1)
73-84: UnusedretryCountparameter and type safety concerns.Three observations:
The
retryCountparameter is added but never used in the function body. If it's intended for future use or consistency with other processors, consider adding a JSDoc comment explaining this.The
abortsignature usesoptions?: unknown, which is very permissive. Consider using a more specific type likeTripWireOptions(if available) for better type safety.Line 84 casts
state.controllertoTransformStreamDefaultController<ChunkType<OUTPUT>>. Sincecontrolleris now optional, this cast could beundefinedat runtime. Consider adding a guard:async processOutputStream(args: { part: ChunkType; streamParts: ChunkType[]; state: Record<string, unknown> & { controller?: TransformStreamDefaultController<ChunkType<OUTPUT>>; }; - abort: (reason?: string, options?: unknown) => never; + abort: (reason?: string, options?: TripWireOptions) => never; // or appropriate type tracingContext?: TracingContext; + /** Retry count for this processing attempt */ retryCount: number; }): Promise<ChunkType | null | undefined> { const { part, state, streamParts, abort, tracingContext } = args; - const controller = state.controller as TransformStreamDefaultController<ChunkType<OUTPUT>>; + const controller = state.controller; + if (!controller) { + throw new Error('Controller is required for structured output processing'); + }packages/core/src/workflows/handlers/control-flow.ts (1)
168-175: Type assertion needed - consider extending StepFailure type.The code correctly preserves the tripwire property when aggregating parallel step results, but uses
(hasFailed as any).tripwireto access it. This suggests theStepFailuretype doesn't include the tripwire field.Consider extending the
StepFailuretype definition (or creating aStepFailureWithTripwirevariant) to include the optionaltripwirefield for better type safety.// In types.ts, extend StepFailure or create a union: export type StepFailure<P, R, S, T> = { status: 'failed'; error: string | Error; payload: P; resumePayload?: R; suspendPayload?: S; suspendOutput?: T; startedAt: number; endedAt: number; suspendedAt?: number; resumedAt?: number; metadata?: StepMetadata; tripwire?: { reason: string; retry?: boolean; metadata?: unknown; processorId?: string }; // Add this };Then the type assertion can be removed:
if (hasFailed) { - // Preserve tripwire property for proper status conversion in fmtReturnValue execResults = { status: 'failed', error: hasFailed.error, - tripwire: (hasFailed as any).tripwire, + tripwire: hasFailed.tripwire, };packages/server/src/server/schemas/workflows.ts (1)
4-14: Tripwire status and processor-workflow flag look consistentAdding
'tripwire'toworkflowRunStatusSchemaandisProcessorWorkflow?: booleantoworkflowInfoSchemacleanly exposes the new runtime state and metadata to the server API. If you rely on generated API docs, consider adding.describe(...)onisProcessorWorkflowto document that it marks auto-generated processor workflows, but the current shape is otherwise solid.Also applies to: 46-56
packages/core/src/processors/examples/content-moderation-workflow.example.ts (1)
1-394: Example stays in sync with new processor & tripwire APIsEven though this file is fully commented, the example usage (typed
Processor<..., TMetadata>,abort(reason, { retry, metadata }),processOutputStepwithretryCount, and tripwire handling on chunks/workflow results) matches the updated processor, TripWire, and workflow semantics in core. No changes needed; just remember to keep this in sync with the live examples underexamples/agentas the APIs evolve.packages/core/src/mastra/index.ts (1)
714-751: Async processor-workflow registration is sound; be aware of eventual consistencyThe new
getConfiguredProcessorWorkflows().then(...)block cleanly auto-registers processor workflows for an agent without blockingaddAgentor the constructor, and it safely swallows/logs failures so a bad processor setup doesn’t crash Mastra initialization.One behavioral nuance: because registration is async, there’s a small window where
addAgent()has returned butaddWorkflow()for those processor workflows hasn’t run yet. If callers immediately querymastra.getWorkflow(...)for a processor workflow right afteraddAgent, they may occasionally miss it. If that matters for consumers, consider documenting this as “eventually consistent” registration or exposing a helper that awaits workflow registration when needed.packages/core/src/workflows/workflow.test.ts (1)
17532-17782: Tripwire workflow tests look aligned; please verify output-stream part shape and consider stream helperThe new
Agent TripWire in Workflowtests correctly exercise:
- Input processor tripwires in non‑streaming runs and
streamVNext- Output stream processor tripwires bubbling up as
status: 'tripwire'withreason,retry,metadata, andprocessorIdTwo things worth double‑checking:
- Shape of
partinprocessOutputStreamconst outputStreamTripwireProcessor = { id: 'output-stream-tripwire-processor', // ... processOutputStream: async ({ part, abort }: any) => { if (part?.type === 'text-delta' && part?.payload?.text?.includes('inappropriate')) { abort('Output contains inappropriate content', { retry: true }); } return part; }, };This assumes the processor sees a
partwithtype === 'text-delta'and a nestedpayload.textfield. Depending on how the agent pipeline wires V2 chunks into processors, the text may instead live under a different key (delta,textDelta, or a differently-shaped payload). If that assumption is wrong, this test will never trigger the abort and will silently assert the wrong behavior.I’d recommend confirming the actual
partshape used byprocessOutputStream(e.g. by inspectingTripWireOptions['processOutputStream']usage inagent/trip-wire.tsand the adapter that feeds model chunks into processors) and updating the predicate accordingly so the test is validating the real path.
- Stream source construction
In these new tests you construct a raw
ReadableStream:stream: new ReadableStream({ /* enqueue chunks */ }),Most other tests in this file use
simulateReadableStreamfrom@internal/ai-sdk-v4, which is already wired for the test environment and avoids subtle differences between Node/web stream implementations. For consistency and to reduce environment coupling, consider switching these three mocks tosimulateReadableStreamas well:stream: simulateReadableStream({ chunks: [ { type: 'stream-start', warnings: [] }, // ... ], });Functionally the tests are solid; these tweaks would make them more robust against implementation detail changes.
examples/agent/src/mastra/agents/model-v2-agent.ts (1)
11-17: New moderation agents are well-configured; consider standardizing import specifiersThe three new agents correctly wire moderation workflows into
inputProcessors, pair them with appropriateoutputProcessors, and setmaxProcessorRetriesconsistently. One minor nit: here you import from'../workflows/content-moderation'whileprocessor-demo.tsuses'./mastra/workflows/content-moderation.js'; standardizing on one style (with or without.js, per your tsconfig/module resolution) would reduce potential confusion.Also applies to: 140-194
packages/core/src/loop/workflows/schema.ts (1)
27-35: Consider tightening runtime validation forreasonto match the new unionYou’ve extended
LLMIterationStepResult.reasontoLanguageModelV2FinishReason | 'tripwire'and correctly addedprocessorRetryCountto both the TypeScript types and Zod schema. To keep runtime validation aligned with the TS type, consider changingllmIterationStepResultSchema.reasonfromz.string()to a concrete enum/union that includes the known finish reasons plus'tripwire'; this would catch invalid reason values earlier.Also applies to: 69-84, 108-148
packages/playground-ui/src/domains/workflows/workflow/workflow-trigger.tsx (1)
195-200: Tripwire status/UI wiring is sound; consider typing tripwire fields to avoidanycastsIncluding
'tripwire'indoneStatuses, derivingdisplayStatusas'tripwire'for failed steps with tripwire data, and passing a normalizedtripwireobject intoWorkflowStatusall look correct and should render processor tripwires clearly in the UI. The only downside is the repeated(step as any).tripwireand(streamResultToUse as any)casts; if feasible, extendingWorkflowRunStreamResultand step types to expose a typedtripwireshape (reason, retry, metadata, processorId) would remove theseanys and catch shape drift at compile time.Also applies to: 330-350, 351-359
packages/server/src/server/handlers/agents.ts (1)
151-162: Consider type narrowing instead ofas anycast.The
(processor as any).namecast works but loses type safety. SinceProcessorWorkflowhas anameproperty, you could use a type guard or check ifisProcessorWorkflowutility exists in the codebase.export function getSerializedProcessors( processors: (InputProcessor | OutputProcessor | InputProcessorOrWorkflow | OutputProcessorOrWorkflow)[], ): SerializedProcessor[] { return processors.map(processor => { - // Processors are class instances or objects with a name property - // Use the name property if available, otherwise fall back to constructor name + // Use the name property if available, otherwise fall back to constructor name + const name = 'name' in processor ? processor.name : processor.constructor.name; return { id: processor.id, - name: (processor as any).name || processor.constructor.name, + name: name || processor.constructor.name, }; }); }packages/core/src/mastra/processor-workflow-registration.test.ts (1)
16-17: Fixed timeout may cause flaky tests.The 50ms
waitForWorkflowRegistrationtimeout could be insufficient in slower CI environments, leading to intermittent test failures. Consider using a polling mechanism or increasing the timeout with a buffer.- // Helper to wait for async workflow registration - const waitForWorkflowRegistration = () => new Promise(resolve => setTimeout(resolve, 50)); + // Helper to wait for async workflow registration with polling + const waitForWorkflowRegistration = async ( + check?: () => boolean, + timeout = 500, + interval = 10 + ) => { + if (!check) { + return new Promise(resolve => setTimeout(resolve, 100)); + } + const start = Date.now(); + while (Date.now() - start < timeout) { + if (check()) return; + await new Promise(resolve => setTimeout(resolve, interval)); + } + };packages/playground-ui/src/domains/workflows/workflow/workflow-step-action-bar.tsx (1)
70-70: Arbitrary Tailwind values used instead of design tokens.Per coding guidelines, use design tokens from
src/ds/tokens/index.tsinstead of arbitrary values likebg-amber-900/40andborder-amber-500/20. If amber tokens don't exist, consider adding them to the design system.As per coding guidelines: "Use design tokens from src/ds/tokens/index.ts instead of arbitrary Tailwind values."
packages/playground-ui/src/components/assistant-ui/messages/tripwire-notice.tsx (2)
73-75: Remove redundantString()wrapper.
JSON.stringify()already returns a string, making theString()wrapper unnecessary.<pre className="text-xs text-amber-200/80 bg-amber-900/30 rounded p-2 overflow-x-auto font-mono"> - {String(JSON.stringify(tripwire.tripwirePayload, null, 2))} + {JSON.stringify(tripwire.tripwirePayload, null, 2)} </pre>
18-82: Arbitrary Tailwind values used instead of design tokens.The component uses numerous arbitrary amber color values (e.g.,
amber-500/30,amber-950/20,amber-400). Per coding guidelines, design tokens fromsrc/ds/tokens/index.tsshould be used. Consider adding tripwire/warning tokens to the design system if they don't exist.As per coding guidelines: "Use design tokens from src/ds/tokens/index.ts instead of arbitrary Tailwind values."
packages/playground-ui/src/domains/workflows/workflow/workflow-default-node.tsx (1)
81-81: Arbitrary Tailwind values used.Similar to other components,
bg-amber-950/40andborder-amber-500/30should use design tokens per coding guidelines.As per coding guidelines: "Use design tokens from src/ds/tokens/index.ts instead of arbitrary Tailwind values."
packages/core/src/stream/base/output.ts (1)
988-1015: Full output text aggregation excludes rejected steps; consider aligning withtextgetter.Computing
textFromStepsfromsteps.map(step => step.text || '')and using it asfullOutput.textis the right way to ensure tripwired steps (whosetextis now blank) don’t leak into the final text while still returning the fullstepsarray.Note that
await stream.textstill resolves from the buffered raw text, which may include rejected content. If consumers are expected to rely only ongetFullOutput().textfor safe text, this is fine; otherwise, consider in a follow‑up aligning thetextgetter with the step‑based aggregation to avoid divergent behaviors.packages/playground-ui/src/domains/workflows/workflow/workflow-status.tsx (1)
82-149: TripwireDetails UX and structure look good; minor styling nit.The expandable TripwireDetails panel (reason, retry indicator, processor id, JSON metadata) is clear and compact, and correctly keyed off the new
TripwireInfocontract.If you want to align more strictly with the design‑token guideline in
packages/playground-ui, you could later replace hard‑coded Tailwind colors likebg-amber-950/20andtext-green-400with tokenized classes (e.g.,bg-surface*,text-accent*) to keep styling consistent with the rest of the DS.docs/processor-enhancements-plan.md (1)
369-399: Consider removing "visually distinguished" phrasing.Per coding guidelines, avoid marketing-adjacent language. Consider rephrasing:
-Processor workflows are visually distinguished in the playground UI with a purple "Processor" badge: +Processor workflows display a purple "Processor" badge in the playground UI:This is a minor stylistic suggestion.
examples/agent/src/mastra/processors/index.ts (1)
98-98: Remove debug console.log statement.This appears to be leftover debug code. Consider using the logger passed through the processor context or removing it entirely.
- console.log('detectedCategories', detectedCategories);packages/core/src/workflows/workflow.ts (1)
414-439: Consider consolidating duplicate error creation logic.The error handling for missing
messageListis repeated across multiple phases (lines 417-424, 444-451, 483-490, 506-512). Consider extracting a helper function.// Helper at the top of the execute function const requireMessageList = (phase: string): MessageList => { if (!resolvedMessageList) { throw new MastraError({ category: ErrorCategory.USER, domain: ErrorDomain.MASTRA_WORKFLOW, id: 'PROCESSOR_MISSING_MESSAGE_LIST', text: `Processor ${processor.id} requires messageList or messages for ${phase} phase`, }); } return resolvedMessageList; }; // Then use in each phase: case 'input': { if (processor.processInput) { const messageList = requireMessageList('processInput'); // ... } }examples/agent/src/mastra/workflows/content-moderation.ts (2)
83-83: Suppress or document the intentional control character regex.The regex
/[^\x00-\x7F]/intentionally uses control character ranges to detect non-ASCII characters. While static analysis flags this, it's a valid pattern for language detection. Consider adding a comment to clarify intent.- const hasNonEnglish = /[^\x00-\x7F]/.test(text); + // Regex matches any character outside ASCII range (0x00-0x7F) to detect non-English text + // biome-ignore lint/suspicious/noControlCharactersInRegex: Intentional ASCII range check + const hasNonEnglish = /[^\x00-\x7F]/.test(text);
244-256: Branch conditions could use more robust input handling.The branch condition accesses
data.messageswithout null checking. IfinputDatalacks amessagesproperty,JSON.stringify(undefined)returnsundefinedwhich may cause unexpected behavior.[ async ({ inputData }) => { const data = inputData as any; - const text = JSON.stringify(data.messages || []); + const messages = data?.messages ?? []; + const text = JSON.stringify(messages); return text.includes('@') || /\d{3}/.test(text); }, piiStep, ],packages/core/src/processors/runner.ts (1)
546-591: Large code block duplicates logic from runOutputProcessors.The workflow handling in
runInputProcessors(lines 532-591) closely mirrorsrunOutputProcessors(lines 160-211). Consider extracting a shared helper for applying workflow results to a messageList.A helper method could reduce duplication:
private applyWorkflowMessagesToList( resultMessages: MastraDBMessage[], messageList: MessageList, idsBeforeProcessing: string[], check: ReturnType<MessageList['makeMessageSourceChecker']>, defaultSource: 'input' | 'response' ): void { const deletedIds = idsBeforeProcessing.filter( (i: string) => !resultMessages.some((m: MastraDBMessage) => m.id === i) ); if (deletedIds.length) { messageList.removeByIds(deletedIds); } for (const message of resultMessages) { messageList.removeByIds([message.id]); if (message.role === 'system') { const systemText = /* extraction logic */; messageList.addSystem(systemText); } else { messageList.add(message, check.getSource(message) || defaultSource); } } }packages/core/src/processors/index.ts (1)
235-252: TheisProcessorWorkflowtype guard is fragile.The type guard uses negative checks (
!('processInput' in obj), etc.) to distinguish workflows from processors. If a new processor method is added in the future and not added to this list, workflows could be incorrectly identified as processors.Consider a positive check approach using a discriminator property:
export function isProcessorWorkflow(obj: unknown): obj is ProcessorWorkflow { return ( obj !== null && typeof obj === 'object' && 'id' in obj && typeof (obj as any).id === 'string' && 'inputSchema' in obj && 'outputSchema' in obj && 'execute' in obj && typeof (obj as any).execute === 'function' && - // Must NOT have processor-specific methods (to distinguish from Processor) - !('processInput' in obj) && - !('processInputStep' in obj) && - !('processOutputStream' in obj) && - !('processOutputResult' in obj) && - !('processOutputStep' in obj) + // Workflows have a 'type' property that processors don't have + 'type' in obj ); }Alternatively, add a
__kind: 'workflow'discriminator to the Workflow class.packages/core/src/agent/agent.ts (1)
348-353: Mutating passed-in workflow'stypeproperty may cause side effects.The workflow passed in is directly mutated (
workflow.type = 'processor'). If the same workflow instance is reused elsewhere (e.g., as a standalone workflow), this mutation could unexpectedly change its type in those contexts.Consider creating a shallow copy or documenting that this mutation is intentional:
if (processors.length === 1 && isProcessorWorkflow(processors[0]!)) { const workflow = processors[0]!; - // Mark the workflow as a processor workflow - workflow.type = 'processor'; + // Mark the workflow as a processor workflow (mutates the original) + // This is intentional as processor workflows should be dedicated to processor use + (workflow as any).type = 'processor'; return [workflow]; }Alternatively, if the workflow should remain unmodified for reuse, consider cloning or wrapping it.
Also applies to: 363-368
packages/core/src/processors/step-schema.ts (1)
215-219: Note:z.custom()provides no runtime validation.
z.custom<MessageList>()provides TypeScript type information but performs no runtime validation. Any value will pass. This is acceptable for internal use where MessageList instances are guaranteed, but worth documenting if this schema is used in workflow definitions that could receive external input.Consider adding a runtime check if external input is possible:
const messageListSchema = z.custom<MessageList>( (val) => val != null && typeof val === 'object' && 'add' in val );
packages/core/src/loop/workflows/agentic-execution/llm-execution-step.ts
Show resolved
Hide resolved
packages/playground-ui/src/components/ui/autoform/zodProvider/field-type-inference.ts
Show resolved
Hide resolved
packages/playground-ui/src/domains/workflows/workflow/workflow-default-node.tsx
Outdated
Show resolved
Hide resolved
packages/playground-ui/src/domains/workflows/workflow/workflow-status.tsx
Show resolved
Hide resolved
packages/playground-ui/src/domains/workflows/workflow/workflow-step-action-bar.tsx
Outdated
Show resolved
Hide resolved
The conditional was inverted - returning undefined when metadata exists. processorId is always available on TripWire regardless of metadata presence. PR comment: #10947 (comment) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
Replace unsafe `as any` casts with proper type narrowing when extracting tripwire data from workflow results. PR comment: #10947 (comment) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
The processor overload should use ProcessorStepOutputSchema for output, not ProcessorStepSchema, to match the actual return type. PR comment: #10947 (comment) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
In Zod v3, ZodLiteral stores the literal at _def.value (singular), not _def.values. This was causing all literal fields to be inferred as 'string' instead of their actual types. PR comment: #10947 (comment) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
- Add 'tripwire' | 'retry' to FinishPayload.stepResult.reason type - Add 'retry' to LLMIterationStepResult.reason type - Update StreamPart.finishReason to include processor reasons - Use @ts-expect-error with clear explanation for zod schema mismatch - Cast to LanguageModelV2FinishReason when converting to AI SDK format PR comment: #10947 (comment) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
Zod v3 uses _def.value (singular) while Zod v4 uses _zod.def.values (array). Handle both to ensure field type inference works across versions. PR comment: #10947 (comment) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
- Add StepTripwireInfo interface for tripwire data on failed steps - Add tripwire property to StepFailure type - Add reason, retry, metadata, processorId to WorkflowRunState - Remove as any casts in utils.ts now that types are properly defined PR comments: - #10947 (comment) - #10947 (comment) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
The Step type already has tripwire property defined, so the cast is not needed. Use !== undefined for the condition to be more explicit. PR comment: #10947 (comment) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
Use strict undefined checks instead of truthiness to ensure falsy values like 0, '', or false are properly detected as "defined metadata". PR comment: #10947 (comment) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
Import TripwireData from the shared location in use-current-run.tsx instead of re-declaring it, avoiding potential divergence. PR comment: #10947 (comment) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Nitpick comments (9)
packages/core/src/loop/workflows/agentic-execution/llm-execution-step.ts (1)
900-905: Consider: Multiple retry feedback messages may accumulate.On each retry, a new feedback message is added via
messageList.addSystem(). If the processor rejects multiple times before reachingmaxProcessorRetries, the context will contain multiple feedback messages. This may be intentional to provide rejection history, but could also clutter the LLM context.Consider whether previous feedback should be removed/replaced on subsequent retries, or if accumulation is the desired behavior.
packages/core/src/workflows/types.ts (1)
283-290: Consider usingRecord<string, unknown>for metadata consistency.The
metadatafield is typed asunknownhere, butStepTripwireInfo.metadatais typed asRecord<string, unknown>. Consider aligning these types for consistency across the codebase./** Tripwire metadata when status is 'tripwire' */ - metadata?: unknown; + metadata?: Record<string, unknown>;packages/core/src/workflows/workflow.ts (1)
346-364: Implicit type assertion may mask schema mismatches at runtime.Casting
inputData(typed asz.infer<typeof ProcessorStepSchema>) directly toProcessorStepOutputbypasses the discriminated union's type safety. If the input doesn't actually contain the expected fields for a given phase, this could lead to runtime issues with undefined values.Consider adding runtime validation or using the discriminated union properly:
- const input = inputData as ProcessorStepOutput; + // The discriminated union is validated at schema level; + // access properties via inputData directly to preserve type narrowing const { phase, messages, - messageList, // ... other fields - } = input; + } = inputData; + // messageList may need explicit handling since it's not serializable + const messageList = (inputData as any).messageList;packages/core/src/processors/runner.ts (4)
131-139: Consider including the underlying error details in the MastraError.When the workflow fails with a non-success status (other than 'tripwire'), the error message only includes the status but not any underlying error details from the result. This could make debugging harder.
if (result.status !== 'success') { + const errorDetails = 'error' in result ? `: ${(result as any).error}` : ''; throw new MastraError({ category: 'USER', domain: 'AGENT', id: 'PROCESSOR_WORKFLOW_FAILED', - text: `Processor workflow ${workflow.id} failed with status: ${result.status}`, + text: `Processor workflow ${workflow.id} failed with status: ${result.status}${errorDetails}`, }); }
325-379: Significant code duplication between workflow and processor handling in stream processing.The workflow handling branch in
processPartduplicates much of the state management and error handling logic from the regular processor branch. Consider extracting common patterns into helper methods to reduce duplication and improve maintainability.
537-598: Repetitive workflow result application logic across methods.The pattern of extracting workflow results and applying them to messageList (checking for deleted IDs, handling system messages, re-adding messages) is duplicated across
runOutputProcessors,runInputProcessors,runProcessInputStep, andrunProcessOutputStep.Consider extracting this into a private helper method:
private applyWorkflowResultToMessageList( workflowResult: ProcessorStepOutput, messageList: MessageList, idsBeforeProcessing: string[], sourceChecker: ReturnType<MessageList['makeMessageSourceChecker']>, defaultSource: 'input' | 'response', ): void { if (!workflowResult.messages) return; const resultMessages = workflowResult.messages as MastraDBMessage[]; const deletedIds = idsBeforeProcessing.filter( (i: string) => !resultMessages.some((m: MastraDBMessage) => m.id === i), ); // ... rest of the logic }
1178-1196: Error handling includes tripwire metadata in span but could be more consistent.The error handling for TripWire in
runProcessOutputStepincludesmetadatain the span output, which is good. However, other similar catch blocks (e.g., inrunProcessInputStepat line 943) don't include the metadata. Consider making this consistent across all methods.packages/playground-ui/src/domains/workflows/workflow/workflow-step-action-bar.tsx (1)
62-63: Align tripwire styling with design tokens instead of raw amber Tailwind colorsThe new tripwire styles use raw Tailwind palette classes (
bg-amber-900/40,border-amber-500/20,text-amber-400,hover:text-amber-300). To keeppackages/playground-uiconsistent with the design system, prefer token-based classes (like the existingbg-surface*,text-icon*,bg-accent*) wired throughsrc/ds/tokens/index.tsinstead of directly using palette colors. Consider introducing dedicated “warning/tripwire” tokens and referencing those here.As per coding guidelines, Tailwind usage in
packages/playground-uishould be driven by design tokens rather than ad-hoc color classes.Also applies to: 168-183
packages/playground-ui/src/domains/workflows/workflow/workflow-default-node.tsx (1)
74-85: Avoidas anyon status and prefer token-based tripwire colorsTwo points to tighten up here:
Type safety for status
status={displayStatus as any}bypasses strict typing. It would be better to narrowdisplayStatusto theWorkflowStepActionBarProps['status'] | undefinedunion (or a localDisplayStatustype) instead of casting toany, then pass that typed value through. This keeps strict mode useful and avoids masking status mismatches.Tripwire color tokens
The node container and icon usebg-amber-950/40,border-amber-500/30, andtext-amber-400. Similar to the action bar, these raw Tailwind amber colors should ideally be replaced with design-token-driven classes (e.g., a warning/tripwire token defined insrc/ds/tokens/index.ts) to stay consistent with the design system.As per coding guidelines,
packages/playground-uicomponents should avoidanycasts where possible under strict TS and should use design tokens rather than ad-hoc Tailwind color values.Also applies to: 124-129, 165-165
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (13)
packages/core/src/loop/workflows/agentic-execution/llm-execution-step.ts(8 hunks)packages/core/src/loop/workflows/schema.ts(3 hunks)packages/core/src/loop/workflows/stream.ts(2 hunks)packages/core/src/processors/runner.ts(17 hunks)packages/core/src/stream/aisdk/v5/transform.ts(2 hunks)packages/core/src/stream/types.ts(6 hunks)packages/core/src/workflows/types.ts(8 hunks)packages/core/src/workflows/workflow.ts(12 hunks)packages/playground-ui/src/components/ui/autoform/zodProvider/field-type-inference.ts(1 hunks)packages/playground-ui/src/domains/workflows/utils.ts(2 hunks)packages/playground-ui/src/domains/workflows/workflow/workflow-default-node.tsx(5 hunks)packages/playground-ui/src/domains/workflows/workflow/workflow-status.tsx(2 hunks)packages/playground-ui/src/domains/workflows/workflow/workflow-step-action-bar.tsx(5 hunks)
🚧 Files skipped from review as they are similar to previous changes (5)
- packages/core/src/loop/workflows/stream.ts
- packages/core/src/loop/workflows/schema.ts
- packages/playground-ui/src/domains/workflows/workflow/workflow-status.tsx
- packages/playground-ui/src/components/ui/autoform/zodProvider/field-type-inference.ts
- packages/playground-ui/src/domains/workflows/utils.ts
🧰 Additional context used
📓 Path-based instructions (6)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Run
pnpm typecheckto validate TypeScript types across all packages
Files:
packages/core/src/stream/aisdk/v5/transform.tspackages/playground-ui/src/domains/workflows/workflow/workflow-default-node.tsxpackages/core/src/loop/workflows/agentic-execution/llm-execution-step.tspackages/core/src/stream/types.tspackages/playground-ui/src/domains/workflows/workflow/workflow-step-action-bar.tsxpackages/core/src/workflows/types.tspackages/core/src/workflows/workflow.tspackages/core/src/processors/runner.ts
**/*.{ts,tsx,js,jsx,json,md}
📄 CodeRabbit inference engine (CLAUDE.md)
Run
pnpm prettier:formatto format code andpnpm formatto run linting with auto-fix across all packages
Files:
packages/core/src/stream/aisdk/v5/transform.tspackages/playground-ui/src/domains/workflows/workflow/workflow-default-node.tsxpackages/core/src/loop/workflows/agentic-execution/llm-execution-step.tspackages/core/src/stream/types.tspackages/playground-ui/src/domains/workflows/workflow/workflow-step-action-bar.tsxpackages/core/src/workflows/types.tspackages/core/src/workflows/workflow.tspackages/core/src/processors/runner.ts
packages/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
packages/**/*.{ts,tsx}: All packages must use TypeScript with strict type checking enabled
Use telemetry decorators for observability across components
Files:
packages/core/src/stream/aisdk/v5/transform.tspackages/playground-ui/src/domains/workflows/workflow/workflow-default-node.tsxpackages/core/src/loop/workflows/agentic-execution/llm-execution-step.tspackages/core/src/stream/types.tspackages/playground-ui/src/domains/workflows/workflow/workflow-step-action-bar.tsxpackages/core/src/workflows/types.tspackages/core/src/workflows/workflow.tspackages/core/src/processors/runner.ts
packages/playground-ui/src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (packages/playground-ui/CLAUDE.md)
packages/playground-ui/src/**/*.{ts,tsx}: Use Tailwind CSS (v3.x) for all styling in packages/playground-ui components
Use design tokens from src/ds/tokens/index.ts instead of arbitrary Tailwind values (e.g., do not use bg-[#1A1A1A])
Use PascalCase for component names (e.g., EntryList)
Use kebab-case for component filenames (e.g., entry-list.tsx)
Use named exports only; avoid default exports in components
Use TanStack Query for all data fetching hooks in packages/playground-ui
Use useMastraClient SDK for API calls instead of direct fetch() calls
Export explicit prop types separately from components (e.g., export type ComponentProps = {...})
Prefer derived values over useState + useEffect; calculate values directly when possible
Keep type definitions alongside components within the same file
Minimize useEffect usage in components; only use it when necessary
packages/playground-ui/src/**/*.{ts,tsx}: Use Tailwind CSS v3.x for all styling inpackages/playground-ui
Use design tokens fromsrc/ds/tokens/index.tsfor Tailwind styling inpackages/playground-ui; forbidden to use arbitrary values (e.g.,bg-[#1A1A1A]) unless explicitly requested
Use PascalCase for component names inpackages/playground-ui
Use kebab-case for component file names inpackages/playground-ui
Use named exports only; avoid default exports inpackages/playground-ui
Use TanStack Query for all data-fetching hooks inpackages/playground-ui; forbidden to use directfetch()calls
UseuseMastraClientSDK for API calls in data-fetching hooks inpackages/playground-ui
Export explicit prop types separately from components inpackages/playground-ui; keep type definitions alongside components
Prefer derived values overuseState+useEffectinpackages/playground-ui; minimizeuseEffectusage and calculate values directly when possible
Use TanStack Query for all server state management inpackages/playground-ui
Files:
packages/playground-ui/src/domains/workflows/workflow/workflow-default-node.tsxpackages/playground-ui/src/domains/workflows/workflow/workflow-step-action-bar.tsx
packages/playground-ui/src/**/*.{tsx,css,scss}
📄 CodeRabbit inference engine (packages/playground-ui/CLAUDE.md)
Prefer Tailwind utilities over custom CSS files for shadows, gradients, and other styles
Files:
packages/playground-ui/src/domains/workflows/workflow/workflow-default-node.tsxpackages/playground-ui/src/domains/workflows/workflow/workflow-step-action-bar.tsx
packages/playground-ui/src/**/*.{tsx,css}
📄 CodeRabbit inference engine (packages/playground-ui/.cursor/rules/frontend.mdc)
Prefer Tailwind utilities over custom CSS files for shadows, gradients, and other styling; only use CSS files when Tailwind cannot express the style
Files:
packages/playground-ui/src/domains/workflows/workflow/workflow-default-node.tsxpackages/playground-ui/src/domains/workflows/workflow/workflow-step-action-bar.tsx
🧠 Learnings (5)
📚 Learning: 2025-11-24T16:41:10.784Z
Learnt from: CR
Repo: mastra-ai/mastra PR: 0
File: packages/playground-ui/.cursor/rules/frontend.mdc:0-0
Timestamp: 2025-11-24T16:41:10.784Z
Learning: Applies to packages/playground-ui/src/**/*.{ts,tsx} : Export explicit prop types separately from components in `packages/playground-ui`; keep type definitions alongside components
Applied to files:
packages/playground-ui/src/domains/workflows/workflow/workflow-step-action-bar.tsx
📚 Learning: 2025-11-24T16:40:41.785Z
Learnt from: CR
Repo: mastra-ai/mastra PR: 0
File: packages/playground-ui/CLAUDE.md:0-0
Timestamp: 2025-11-24T16:40:41.785Z
Learning: Applies to packages/playground-ui/src/**/*.{ts,tsx} : Export explicit prop types separately from components (e.g., export type ComponentProps = {...})
Applied to files:
packages/playground-ui/src/domains/workflows/workflow/workflow-step-action-bar.tsx
📚 Learning: 2025-11-24T16:40:52.090Z
Learnt from: CR
Repo: mastra-ai/mastra PR: 0
File: packages/playground/CLAUDE.md:0-0
Timestamp: 2025-11-24T16:40:52.090Z
Learning: Applies to packages/playground/**/*.tsx : REQUIRED: Export explicit prop types separately as a type definition in packages/playground components
Applied to files:
packages/playground-ui/src/domains/workflows/workflow/workflow-step-action-bar.tsx
📚 Learning: 2025-11-24T16:41:20.908Z
Learnt from: CR
Repo: mastra-ai/mastra PR: 0
File: packages/playground/.cursor/rules/frontend.mdc:0-0
Timestamp: 2025-11-24T16:41:20.908Z
Learning: Applies to packages/playground/src/**/*.{ts,tsx} : Export explicit prop types separately from component definitions
Applied to files:
packages/playground-ui/src/domains/workflows/workflow/workflow-step-action-bar.tsx
📚 Learning: 2025-10-29T09:37:59.778Z
Learnt from: TheIsrael1
Repo: mastra-ai/mastra PR: 9247
File: packages/core/src/loop/network/index.ts:357-393
Timestamp: 2025-10-29T09:37:59.778Z
Learning: In the agent network routing completion stream (packages/core/src/loop/network/index.ts), when `completionStream.error` is true, the first stream should not have written any text (currentTextIdx should be 0), so the fallback reset of currentText and currentTextIdx does not cause duplication.
Applied to files:
packages/core/src/workflows/workflow.ts
🧬 Code graph analysis (7)
packages/playground-ui/src/domains/workflows/workflow/workflow-default-node.tsx (1)
packages/playground/src/lib/utils.ts (1)
cn(6-8)
packages/core/src/loop/workflows/agentic-execution/llm-execution-step.ts (2)
packages/core/src/agent/trip-wire.ts (1)
TripWire(35-45)packages/core/src/processors/runner.ts (1)
ProcessorRunner(71-1201)
packages/core/src/stream/types.ts (3)
packages/core/src/llm/model/base.types.ts (1)
ToolSet(29-29)packages/core/src/processors/index.ts (1)
OutputProcessorOrWorkflow(227-229)packages/core/src/workflows/types.ts (1)
StepResult(122-127)
packages/playground-ui/src/domains/workflows/workflow/workflow-step-action-bar.tsx (2)
packages/playground-ui/src/domains/workflows/context/use-current-run.tsx (1)
TripwireData(4-11)packages/core/src/stream/base/output.ts (2)
tripwire(1025-1027)status(1266-1268)
packages/core/src/workflows/types.ts (2)
observability/mastra/src/model-tracing.ts (1)
Record(203-205)packages/core/src/observability/types/tracing.ts (1)
TracingProperties(795-798)
packages/core/src/workflows/workflow.ts (3)
packages/core/src/processors/index.ts (4)
Processor(130-184)ProcessorStepSchema(259-259)ProcessorStepOutputSchema(261-261)ProcessorStepOutput(286-286)packages/core/src/processors/step-schema.ts (3)
ProcessorStepSchema(416-416)ProcessorStepOutputSchema(386-410)ProcessorStepOutput(439-439)packages/core/src/workflows/types.ts (1)
WorkflowType(24-24)
packages/core/src/processors/runner.ts (7)
packages/core/src/processors/index.ts (5)
Processor(130-184)ProcessorWorkflow(215-215)ProcessorStepOutput(286-286)isProcessorWorkflow(235-252)TripWireOptions(257-257)packages/core/src/processors/step-schema.ts (1)
ProcessorStepOutput(439-439)packages/core/src/observability/types/tracing.ts (1)
TracingContext(787-790)packages/core/src/agent/trip-wire.ts (2)
TripWire(35-45)TripWireOptions(14-25)packages/core/src/agent/message-list/index.ts (1)
MastraDBMessage(58-60)packages/core/src/memory/types.ts (1)
MastraDBMessage(6-6)packages/core/src/stream/types.ts (1)
ChunkType(624-624)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (28)
packages/core/src/loop/workflows/agentic-execution/llm-execution-step.ts (7)
474-475: LGTM!The
maxProcessorRetriesparameter with a default of 3 is a reasonable limit. The parameter integrates cleanly with the existing function signature.
718-744: Verify: Using 'tripwire' reason for user-initiated abort.This bail path is triggered when the abort signal is received (user cancellation). Changing the reason from
'abort'to'tripwire'may conflate processor-triggered tripwires with user-initiated aborts. Consider whether a distinct reason like'abort'should be preserved here for clarity.
788-835: LGTM!The processOutputStep integration is well-structured:
- TripWire errors are captured separately for retry handling
- Other errors are re-thrown appropriately
- The step context (text, toolCalls, finishReason, retryCount) is correctly assembled
843-861: LGTM!The retry decision logic correctly:
- Combines both tripwire sources (stream and processOutputStep)
- Validates retry eligibility against
maxProcessorRetries- Logs a warning when retry limit is exceeded for debugging visibility
873-882: Verify: Tripwire data only captured from processOutputStep.The
stepTripwireDataonly captures tripwire information fromprocessOutputStepTripwire, buttripwireTriggeredat line 844 includesoutputStream.tripwireas well. If a tripwire is triggered viaoutputStream.tripwire(stream processors), the step result won't contain tripwire details (message, metadata, processorId).Is this intentional? If stream-triggered tripwires should also populate step tripwire data, consider:
const stepTripwireData = processOutputStepTripwire ? { message: processOutputStepTripwire.message, retry: processOutputStepTripwire.options?.retry, metadata: processOutputStepTripwire.options?.metadata, processorId: processOutputStepTripwire.processorId, } + : outputStream.tripwire + ? { + message: outputStream.tripwire.message, + retry: outputStream.tripwire.options?.retry, + metadata: outputStream.tripwire.options?.metadata, + processorId: outputStream.tripwire.processorId, + } : undefined;
913-958: LGTM!The step result computation is well-designed:
- The
stepReasonpriority chain (retry → tripwire → error → finishReason) correctly prioritizes retry stateshouldContinueproperly allows the loop to continue on retry or tool-use scenarios- Clearing
toolCallson retry prevents duplicate tool executionsprocessorRetryCountis correctly incremented and propagatedThe previous review comment about
retryProcessorIdhas been addressed—it now directly accessesprocessOutputStepTripwire.processorId.
477-480: Type definitions are correctly implemented.The code properly accesses
inputData.processorRetryCountand passestripwiretoDefaultStepResult. Both fields are correctly typed:
llmIterationOutputSchemaincludesprocessorRetryCount: z.number().optional()(line 148)DefaultStepResultacceptstripwire?: StepTripwireDatain its constructor optionspackages/core/src/workflows/types.ts (3)
19-24: LGTM!The
WorkflowTypeunion type is well-documented and provides clear discriminant values for workflow categorization in the UI.
66-72: LGTM!The
StepTripwireInfointerface captures all necessary tripwire metadata fields with appropriate optionality. TheRecord<string, unknown>for metadata provides flexibility while maintaining type safety.
486-509: No changes needed —processorIdis already correctly typed as optional.The
processorIdfield in the tripwireWorkflowResultvariant is already optional (processorId?: string), consistent withStepTripwireInfo.processorIdand theTripWireclass constructor. The codebase correctly reflects that tripwires can be triggered with or without a processor ID.Likely an incorrect or invalid review comment.
packages/core/src/workflows/workflow.ts (4)
143-154: LGTM! Output schema correctly usesProcessorStepOutputSchema.The processor overload signature correctly specifies
ProcessorStepOutputSchemaas the output schema type, matching the return type of the execute function implementation. This addresses the previous review comment.
241-285: LGTM! Proper TripWire propagation from agent streams.The tripwire detection and re-throwing mechanism correctly propagates tripwire data (reason, retry, metadata, processorId) from stream chunks, ensuring the workflow can handle processor rejections appropriately.
573-591: Type guard relies on method existence but doesn't verify method signatures.The
isProcessortype guard checks for the existence of processor methods but doesn't validate their signatures. An object with anidand aprocessInputproperty that's a function but with wrong arguments would pass the guard. This is acceptable for runtime duck-typing but worth noting.
407-410: No action needed. The MessageList instantiation pattern used here is correct for UI-triggered workflows. Whennew MessageList().add(messages, 'input')is called, the tracking Sets (memoryMessages, newUserMessages, etc.) are properly initialized and the 'input' source parameter correctly marks messages in the newUserMessages set. Recording state is managed by the ProcessorRunner (which callsstartRecording()before each processor execution), not expected to be inherited from the MessageList constructor. This is the intended pattern when messageList cannot be serialized from the UI.Likely an incorrect or invalid review comment.
packages/core/src/processors/runner.ts (3)
112-129: LGTM! Type-safe tripwire extraction from workflow result.The code properly narrows the result type before accessing tripwire properties, addressing the previous review concern about unsafe type assertions.
972-1011: LGTM! Well-documentedrunProcessOutputStepmethod.The JSDoc documentation clearly explains the method's purpose, parameters, and when it runs in the agentic loop. The implementation correctly handles the
outputStepphase for both workflows and regular processors.
66-92: LGTM! Clean type union and constructor update.The
ProcessorOrWorkflowtype union cleanly expresses the dual nature of processors, and the constructor properly accepts the new type for both input and output processors.packages/playground-ui/src/domains/workflows/workflow/workflow-step-action-bar.tsx (1)
8-22: Tripwire wiring and dialog behavior look coherentImporting
TripwireDatafrom the shared context, extendingWorkflowStepActionBarPropswithtripwireand the'tripwire'status, and addingisTripwireOpenplus the Tripwire dialog are all consistent with the existing input/output/error patterns. The top-level visibility guard now includingtripwireensures the action bar appears when only tripwire data is present, and the dialog payload (reason/retry/metadata/processorId) matches the shared TripwireData shape. No functional issues noted.Also applies to: 31-45, 56-66, 166-189
packages/playground-ui/src/domains/workflows/workflow/workflow-default-node.tsx (1)
55-58: Tripwire detection and propagation into the action bar look correctDeriving
isTripwirefrom a failed step that hasstep.tripwire, mapping that to adisplayStatusof'tripwire', and then passingerror={isTripwire ? undefined : step?.error}withtripwire={isTripwire ? step?.tripwire : undefined}intoWorkflowStepActionBarcleanly separates tripwire from normal failure. This matches the intent to treat tripwire as a distinct status while still preserving the underlying step data. No behavioral issues spotted here.Also applies to: 162-164
packages/core/src/stream/types.ts (7)
16-16: LGTM! Import aligns with workflow-as-processor support.The change from
OutputProcessortoOutputProcessorOrWorkflowcorrectly supports the processor workflow functionality introduced in this PR.
171-195: LGTM! Tripwire support properly integrated.The generic typing over
Toolsand extension ofreasonto include 'tripwire' | 'retry' correctly implements the processor tripwire feature. The switch toMastraStepResult<Tools>[]appropriately propagates tripwire data through the steps array.
220-251: LGTM! Consistent tripwire propagation in step finish payload.The update to
MastraStepResult<Tools>[]is consistent with theFinishPayloadchanges and ensures tripwire data flows through step finish events.
309-317: LGTM! Well-typed tripwire metadata.The generic
TMetadataparameter and optional fields (retry,metadata,processorId) provide good type safety while maintaining flexibility. The documentation clearly explains each field's purpose.
682-695: LGTM! Output options support processor workflows.The change to
OutputProcessorOrWorkflow[]correctly enables the workflow-as-processor functionality described in the PR objectives.
697-719: LGTM! Clean type extension for tripwire support.The
StepTripwireDatainterface andMastraStepResulttype are well-designed:
- Clear structure for tripwire metadata
- Proper extension of AI SDK's
StepResulttype- Excellent documentation
- Maintains compatibility while adding Mastra-specific functionality
755-756: LGTM! LLMStepResult gains tripwire support.The addition of the optional
tripwirefield is consistent with theMastraStepResultpattern and properly typed.packages/core/src/stream/aisdk/v5/transform.ts (2)
14-27: LGTM! StreamPart correctly extends finish reason.The extension of
finishReasonto include 'tripwire' | 'retry' is consistent with the type changes intypes.tsand properly documented.
362-369: The cast at line 366 is a necessary architectural bridge between Mastra's internal extended format and AI SDK v5's standard format. No action required.The type cast narrowing
'tripwire' | 'retry'away toLanguageModelV2FinishReasonis intentional and safe. AI SDK v5 gracefully maps unknown finish reason values to the "unknown" sentinel rather than throwing errors. The current implementation is correct: the internal Mastra format (StepResult.reason) supports extended reasons for processor scenarios, but when converting to AI SDK v5 format, these must be narrowed to standard reasons. The comment already documents this necessity.Likely an incorrect or invalid review comment.
- Add MastraFinishReason type that extends LanguageModelV2FinishReason with 'tripwire' | 'retry' for processor scenarios - Export MastraFinishReason from @mastra/core/stream - Fix @mastra/ai-sdk helpers.ts to cast to AISDKFinishReason when converting Mastra chunks to AI SDK format 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Nitpick comments (1)
packages/core/src/stream/types.ts (1)
177-201: UseMastraFinishReasontype for consistency.Line 180 manually defines
LanguageModelV2FinishReason | 'tripwire' | 'retry', which duplicates theMastraFinishReasontype defined at line 33. Use the named type for DRY and maintainability.interface FinishPayload<Tools extends ToolSet = ToolSet> { stepResult: { /** Includes 'tripwire' and 'retry' for processor scenarios */ - reason: LanguageModelV2FinishReason | 'tripwire' | 'retry'; + reason: MastraFinishReason; warnings?: LanguageModelV2CallWarning[]; isContinued?: boolean; logprobs?: LanguageModelV1LogProbs; };
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
client-sdks/ai-sdk/src/helpers.ts(2 hunks)packages/core/src/stream/index.ts(1 hunks)packages/core/src/stream/types.ts(7 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Run
pnpm typecheckto validate TypeScript types across all packages
Files:
packages/core/src/stream/index.tsclient-sdks/ai-sdk/src/helpers.tspackages/core/src/stream/types.ts
**/*.{ts,tsx,js,jsx,json,md}
📄 CodeRabbit inference engine (CLAUDE.md)
Run
pnpm prettier:formatto format code andpnpm formatto run linting with auto-fix across all packages
Files:
packages/core/src/stream/index.tsclient-sdks/ai-sdk/src/helpers.tspackages/core/src/stream/types.ts
packages/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
packages/**/*.{ts,tsx}: All packages must use TypeScript with strict type checking enabled
Use telemetry decorators for observability across components
Files:
packages/core/src/stream/index.tspackages/core/src/stream/types.ts
🧬 Code graph analysis (1)
packages/core/src/stream/types.ts (4)
packages/core/src/stream/index.ts (1)
MastraFinishReason(9-9)packages/core/src/llm/model/base.types.ts (1)
ToolSet(29-29)packages/core/src/processors/index.ts (1)
OutputProcessorOrWorkflow(227-229)packages/core/src/workflows/types.ts (1)
StepResult(122-127)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (9)
packages/core/src/stream/types.ts (7)
16-16: LGTM!Import correctly updated to support the new workflow-as-processor functionality.
29-33: LGTM!The new
MastraFinishReasontype is well-documented and correctly extends the AI SDK's finish reasons with Mastra-specific values.
226-257: Verify asymmetry instepResult.reasontypes.
FinishPayload.stepResult.reason(line 180) includes'tripwire' | 'retry', butStepFinishPayload.stepResult.reason(line 236) remainsLanguageModelV2FinishReason. If a step can be rejected by a processor, should this also useMastraFinishReason?
315-323: LGTM!The
TripwirePayloadenhancements with generic metadata, retry flag, and processor ID are well-designed and properly documented.
695-695: LGTM!Type correctly updated to support both traditional processors and workflow-based processors.
703-725: LGTM!The
StepTripwireDataandMastraStepResulttypes are well-structured with clear JSDoc documentation. This cleanly extends the AI SDK'sStepResultwith Mastra-specific tripwire data.
761-762: LGTM!Consistent addition of
tripwirefield toLLMStepResult, aligning with the new tripwire data propagation pattern.packages/core/src/stream/index.ts (1)
1-10: These types are not currently consumed by external packages and do not need to be exported.
StepTripwireDatais only used internally withinpackages/core/src/stream/aisdk/v5/output-helpers.tsand is imported directly fromtypes.tsrather than from the barrel file.MastraStepResulthas no external usage. External packages (playground-ui, react, cli) import from@mastra/core/agentand@mastra/core/workflowsbut not from the stream module. Adding these exports without demonstrated external demand would expand the public API unnecessarily.Likely an incorrect or invalid review comment.
client-sdks/ai-sdk/src/helpers.ts (1)
10-14: LGTM: Type union correctly extended.The addition of
DataChunkTypeandundefinedto the union appropriately reflects the return values from theconvertMastraChunkToAISDKv5function.
Add instanceof MessageList check before array/object checks to properly extract messages via result.get.all.db() when a processor returns a MessageList instance. PR comment: #10947 (comment) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
Add toAISDKFinishReason helper function to properly map Mastra's extended
finish reasons ('tripwire' | 'retry') to AI SDK's 'other' value instead
of just casting. Apply to both 'finish' and 'step-finish' cases.
PR comment: #10947 (comment)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/memory/integration-tests-v5/src/processors.test.ts (1)
488-488: Inconsistent model specification format.This test uses
openai('gpt-4o')while the test at line 372 uses the string literal'openai/gpt-4o'. According to the AI summary, the model field was updated to use string literals. Ensure consistent usage across all tests.Apply this diff to align with the updated pattern:
- model: openai('gpt-4o'), + model: 'openai/gpt-4o',
🧹 Nitpick comments (2)
packages/memory/integration-tests-v5/src/processors.test.ts (2)
30-32: Consider aligning abort helper with the enhanced tripwire API.The PR introduces an enhanced abort signature
abort(reason, { retry, metadata }), but this test helper only accepts a reason string. While the current tests don't exercise retry/metadata functionality, consider adding a more complete helper or documenting why the simplified version is sufficient for these integration tests.Example enhancement:
-const abort: (reason?: string) => never = reason => { - throw new Error(reason || 'Aborted'); -}; +const abort = (reason?: string, options?: { retry?: boolean; metadata?: unknown }): never => { + const error: any = new Error(reason || 'Aborted'); + if (options) { + error.retry = options.retry; + error.metadata = options.metadata; + } + throw error; +};
612-678: Address or remove skipped test with TODO.This test is skipped with a TODO at line 650 questioning the test's purpose. The test appears to reference
memory.processMessages()which may no longer exist. Consider either:
- Updating the test to use the current Processor API
- Removing the test if the functionality is covered elsewhere
- Creating a tracking issue to revisit this later
Do you want me to help identify whether this test's functionality is covered by other tests, or open an issue to track this technical debt?
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
docs/src/content/en/reference/processors/processor-interface.mdx(7 hunks)packages/memory/integration-tests-v5/src/processors.test.ts(14 hunks)
🧰 Additional context used
📓 Path-based instructions (6)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Run
pnpm typecheckto validate TypeScript types across all packages
Files:
packages/memory/integration-tests-v5/src/processors.test.ts
**/*.{ts,tsx,js,jsx,json,md}
📄 CodeRabbit inference engine (CLAUDE.md)
Run
pnpm prettier:formatto format code andpnpm formatto run linting with auto-fix across all packages
Files:
packages/memory/integration-tests-v5/src/processors.test.ts
packages/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
packages/**/*.{ts,tsx}: All packages must use TypeScript with strict type checking enabled
Use telemetry decorators for observability across components
Files:
packages/memory/integration-tests-v5/src/processors.test.ts
**/*.{test,spec}.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{test,spec}.{ts,tsx}: Use Vitest for testing framework in test files
Co-locate test files with source code using naming patterns like *.test.ts or *.spec.ts
Files:
packages/memory/integration-tests-v5/src/processors.test.ts
**/*.{md,mdx}
📄 CodeRabbit inference engine (.cursor/rules/writing-documentation.mdc)
**/*.{md,mdx}: When writing developer documentation, do not use adjectives like 'powerful' or 'built-in' as these read like marketing copy and developers don't like that
When writing developer documentation, do not use 'complete', 'out-of-the-box', 'hands-on', or overly enthusiastic exhortations like 'Check out', 'Learn more', 'Explore'. Do not use words like 'essential' or 'offers'
When writing developer documentation, do not use 'your needs', 'production-ready', 'makes it easy', or 'choose the right...solution' as these are marketing jargon that developers dislike
When writing developer documentation, avoid phrasing like 'without changing your code' or 'automatically handles' that obscures implementation details
In developer documentation, avoid phrasing that glides between benefits without diving into details. Focus on technical specifics and implementation details rather than high-level benefits. For example, avoid sentences like: 'This makes it easy to build AI applications that maintain meaningful conversations and remember important details, whether you're building a simple chatbot or a sophisticated AI assistant'
All H1 headings (# Heading) must use title case format, capitalizing the first letter of each major word. Examples: 'Getting Started', 'Human In-the-Loop Workflow', 'Agent as a Step'
Files:
docs/src/content/en/reference/processors/processor-interface.mdx
**/*{docs,documentation}/**/*.{md,mdx}
📄 CodeRabbit inference engine (.windsurfrules)
**/*{docs,documentation}/**/*.{md,mdx}: When writing developer documentation, do not use adjectives like 'powerful' or 'built-in' as they read like marketing copy
When writing developer documentation, do not use words like 'complete', 'out-of-the-box', 'hands-on', or overly enthusiastic exhortations such as 'Check out', 'Learn more', 'Explore', 'essential', or 'offers'
When writing developer documentation, avoid marketing jargon such as 'your needs', 'production-ready', 'makes it easy', or 'choose the right...solution'
When writing developer documentation, do not use phrases like 'without changing your code' or 'automatically handles' as they glide over implementation details
In developer documentation, avoid phrasing that glides between benefits without diving into details; instead, focus on technical nuts and bolts with specific implementation details rather than abstract benefits
Files:
docs/src/content/en/reference/processors/processor-interface.mdx
🧠 Learnings (10)
📚 Learning: 2025-11-24T16:42:04.244Z
Learnt from: CR
Repo: mastra-ai/mastra PR: 0
File: packages/codemod/AGENTS.md:0-0
Timestamp: 2025-11-24T16:42:04.244Z
Learning: Applies to packages/codemod/src/test/**/*.test.ts : Include test cases for multiple occurrences of the transformation pattern, aliased imports, type imports, and mixed imports to verify the codemod works consistently
Applied to files:
packages/memory/integration-tests-v5/src/processors.test.ts
📚 Learning: 2025-11-24T16:42:04.244Z
Learnt from: CR
Repo: mastra-ai/mastra PR: 0
File: packages/codemod/AGENTS.md:0-0
Timestamp: 2025-11-24T16:42:04.244Z
Learning: Applies to packages/codemod/src/test/__fixtures__/**/*.ts : In output fixtures, ensure all NEGATIVE test cases remain EXACTLY IDENTICAL to their input fixture counterparts to verify the codemod only transforms intended patterns
Applied to files:
packages/memory/integration-tests-v5/src/processors.test.ts
📚 Learning: 2025-11-24T16:42:04.244Z
Learnt from: CR
Repo: mastra-ai/mastra PR: 0
File: packages/codemod/AGENTS.md:0-0
Timestamp: 2025-11-24T16:42:04.244Z
Learning: Applies to packages/codemod/src/codemods/v1/**/*.ts : When transforming TypeScript type imports and usages, check the parent node type to skip transforming identifiers that are part of import declarations to avoid transforming imports from other packages with the same type name
Applied to files:
packages/memory/integration-tests-v5/src/processors.test.ts
📚 Learning: 2025-11-24T16:42:04.244Z
Learnt from: CR
Repo: mastra-ai/mastra PR: 0
File: packages/codemod/AGENTS.md:0-0
Timestamp: 2025-11-24T16:42:04.244Z
Learning: Applies to packages/codemod/src/codemods/v1/**/*.ts : When renaming function/value imports and all their usages, use the `renameImportAndUsages()` utility which handles both the import declaration and usages in a single pass, and correctly handles aliased imports
Applied to files:
packages/memory/integration-tests-v5/src/processors.test.ts
📚 Learning: 2025-11-24T16:42:04.244Z
Learnt from: CR
Repo: mastra-ai/mastra PR: 0
File: packages/codemod/AGENTS.md:0-0
Timestamp: 2025-11-24T16:42:04.244Z
Learning: Applies to packages/codemod/src/codemods/v1/**/*.ts : Prefer using shared utility functions (`trackClassInstances()`, `renameMethod()`, `renameMethods()`, `transformMethodCalls()`, `renameImportAndUsages()`, `transformConstructorProperties()`, `transformObjectProperties()`) instead of writing custom AST transformation logic
Applied to files:
packages/memory/integration-tests-v5/src/processors.test.ts
📚 Learning: 2025-11-24T16:42:04.244Z
Learnt from: CR
Repo: mastra-ai/mastra PR: 0
File: packages/codemod/AGENTS.md:0-0
Timestamp: 2025-11-24T16:42:04.244Z
Learning: Applies to packages/codemod/**/*.ts : Always optimize for minimal AST traversals: use shared utility functions from `src/codemods/lib/utils.ts`, combine multiple operations into single passes, add early returns when no changes needed, and track instances once and reuse the Set
Applied to files:
packages/memory/integration-tests-v5/src/processors.test.ts
📚 Learning: 2025-11-24T16:42:04.244Z
Learnt from: CR
Repo: mastra-ai/mastra PR: 0
File: packages/codemod/AGENTS.md:0-0
Timestamp: 2025-11-24T16:42:04.244Z
Learning: Applies to packages/codemod/src/codemods/v1/**/*.ts : When tracking which package an import came from, verify the import source before transforming usages to avoid transforming identifiers from other packages with the same name
Applied to files:
packages/memory/integration-tests-v5/src/processors.test.ts
📚 Learning: 2025-11-24T16:42:04.244Z
Learnt from: CR
Repo: mastra-ai/mastra PR: 0
File: packages/codemod/AGENTS.md:0-0
Timestamp: 2025-11-24T16:42:04.244Z
Learning: Applies to packages/codemod/src/test/**/*.test.ts : Do NOT use `UPDATE_SNAPSHOT` to force tests to pass; instead fix the codemod implementation when tests fail
Applied to files:
packages/memory/integration-tests-v5/src/processors.test.ts
📚 Learning: 2025-11-24T16:42:04.244Z
Learnt from: CR
Repo: mastra-ai/mastra PR: 0
File: packages/codemod/AGENTS.md:0-0
Timestamp: 2025-11-24T16:42:04.244Z
Learning: Applies to packages/codemod/src/test/__fixtures__/**/*.ts : In input fixtures, include both POSITIVE test cases (patterns that should transform) and NEGATIVE test cases (unrelated code with similar names/patterns that should NOT transform)
Applied to files:
packages/memory/integration-tests-v5/src/processors.test.ts
📚 Learning: 2025-11-24T16:42:04.244Z
Learnt from: CR
Repo: mastra-ai/mastra PR: 0
File: packages/codemod/AGENTS.md:0-0
Timestamp: 2025-11-24T16:42:04.244Z
Learning: Applies to packages/codemod/src/test/__fixtures__/**/*.ts : Create test fixtures by copying examples DIRECTLY from migration guides in `docs/src/content/en/guides/migrations/upgrade-to-v1/` without hallucinating or inventing changes
Applied to files:
packages/memory/integration-tests-v5/src/processors.test.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (11)
- GitHub Check: Socket Security: Pull Request Alerts
- GitHub Check: test
- GitHub Check: test
- GitHub Check: Check for deleted pages without redirects
- GitHub Check: test
- GitHub Check: test (express)
- GitHub Check: test (hono)
- GitHub Check: Validate peer dependencies
- GitHub Check: Prebuild
- GitHub Check: Lint
- GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (4)
docs/src/content/en/reference/processors/processor-interface.mdx (2)
376-491: Excellent addition of comprehensive processOutputStep documentation.The new section clearly explains the per-step output processing capability, including the retry mechanism. The example is practical and demonstrates proper use of
retryCountto limit retry attempts and structured metadata passing.Verify that the abort signature with retry/metadata options is consistently implemented across all processor methods (particularly in
processInputStepat line 236-239, which currently shows only(reason?: string) => never). IfprocessInputStepalso supports retry signaling, its documentation should matchprocessOutputStep's abort signature for consistency.
139-148: Clear documentation of retryCount parameter.The new
retryCountparameter is well-documented in bothProcessInputArgs(lines 143-148) and its usage in theprocessOutputStepexample is appropriate. However, ensureprocessInputStepdocumentation (around line 236-240) reflects the same parameter if it's available in both methods.packages/memory/integration-tests-v5/src/processors.test.ts (2)
343-350: LGTM - Clean Processor implementation.The
ConversationOnlyFiltercorrectly implements theProcessorinterface with appropriate id, name, and processInput method. The simple filtering logic is appropriate for this test scenario.
557-600: LGTM - Well-structured processor testing.This refactored section clearly tests processors directly with appropriate comments explaining the behavior. The sequential application of
ToolCallFilterandTokenLimiterwith explicitrequestContextdemonstrates proper processor composition.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 6
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/core/src/agent/agent.ts (1)
3118-3146: Extend tripwire handling toresumeStreamas well.
resumeStream()calls#execute()and then has the sameresult.status !== 'success'check asstream(), but it also lacks an explicit'tripwire'branch. If a resumed run trips a processor guardrail, it will currently surface as an “unknown error” instead of a structuredTripWire.Mirror the
result.status === 'tripwire'handling suggested forgenerate/streamhere too (throwing aTripWirebuilt fromresult.tripwire) before the'failed'/unknown fallbacks so all streaming entry points behave consistently.
♻️ Duplicate comments (3)
packages/core/src/loop/workflows/stream.ts (1)
182-195: Finish chunk semantics look good; consider aligning types to avoid@ts-expect-error.Emitting a
finishchunk even for tripwire/abort cases is a solid change and should make stream consumers more reliable, since they always see a terminal event driven bystepResult.reason.The remaining rough edge here is the
// @ts-expect-erroraroundstepResult.reason. The comment explains that runtime values can be'tripwire' | 'retry'from processors while the Zod-inferred type is juststring/a narrower union. Long‑term, it would be cleaner to update the underlyingStepResult/workflow result types (and/or the Zod schema driving them) so thatreasonformally includes the processor-driven values ('tripwire' | 'retry'), and then drop this suppression.That keeps the runtime behavior you want while letting the type system accurately model it and catch regressions.
packages/core/src/workflows/workflow.test.ts (1)
17898-17905: Align vNext stream typing and drop unusedchunksaccumulator
run.streamVNext(...).fullStreamyields the vNext workflow stream type (likelyWorkflowStreamEventorChunkType), but here it’s collected intoStreamEvent[], which is intended for the legacystreamLegacypath and may fail strict typechecking again. You also never assert onchunks, so the accumulator is effectively dead code.Consider either:
- Switching the type to the correct vNext event type (e.g.
ChunkType[]orWorkflowStreamEvent[]), or- Removing the variable entirely and just draining the stream:
- const chunks: StreamEvent[] = []; const streamResult = run.streamVNext({ inputData: { prompt: 'This has forbidden content' } }); // Collect all chunks - for await (const chunk of streamResult.fullStream) { - chunks.push(chunk); - } + for await (const _chunk of streamResult.fullStream) { + // no-op – just drain the stream to completion + }This keeps the test focused on the workflow-level
tripwireassertion and avoids vNext/legacy type confusion.#!/bin/bash # From repo root, verify the vNext stream type for `run.streamVNext().fullStream` rg -n "streamVNext\(" packages/core/src/workflows -C3 rg -n "type WorkflowStreamEvent" packages/core/src/workflows -C3 rg -n "type ChunkType" packages/core/src/workflows -C3 pnpm typecheck --filter @mastra/corepackages/core/src/agent/agent.ts (1)
2962-2990: Handleresult.status === 'tripwire'explicitly ingenerateandstream.The underlying workflow can now return a
'tripwire'status when processors abort viaTripWire, butgenerate()andstream()still only branch on'success'and'failed', treating'tripwire'as an unknown error and dropping structured tripwire data. This was already flagged earlier and remains unresolved.You can map tripwire results back to a
TripWireerror so callers and UI layers can handle them predictably. For example:const result = await this.#execute(executeOptions); - if (result.status !== 'success') { - if (result.status === 'failed') { + if (result.status !== 'success') { + if (result.status === 'tripwire') { + const tripwire = (result as any).tripwire; + throw new TripWire( + tripwire?.reason ?? 'Agent tripwire triggered', + { retry: tripwire?.retry, metadata: tripwire?.metadata }, + tripwire?.processorId, + ); + } + if (result.status === 'failed') { throw new MastraError( @@ - } - throw new MastraError({ + } + throw new MastraError({ id: 'AGENT_GENERATE_UNKNOWN_ERROR', @@ - }); + }); }Apply the same pattern in
stream()before the generic unknown-error case, using an appropriate error id/message for streaming; this preserves processor tripwire details end‑to‑end.Also applies to: 3051-3069
🧹 Nitpick comments (8)
packages/core/src/workflows/default.ts (1)
267-347: TripWire-to-plain-object conversion inexecuteStepWithRetrylooks correct; consider de-duplicating mapping logicThe new return shape with an optional
error.tripwire?: StepTripwireInfoand thee instanceof TripWireguard correctly preservesreason,retry,metadata, andprocessorIdin a serializable form while still wrapping the error inMastraErrorfor logging/telemetry. This aligns with the PR goal of exposing structured tripwire data without leaking class instances.You now have very similar TripWire→POJO mapping logic here and again in
fmtReturnValue. To avoid drift if theTripWireoptions shape orStepTripwireInfoevolve, consider extracting a small helper (e.g.,toStepTripwireInfo(e: TripWire): StepTripwireInfo) and reusing it in both places.packages/core/src/processors/runner.ts (1)
908-1111: Well-designed implementation of per-step output processing.The new
runProcessOutputStepmethod provides a solid foundation for guardrails and retry logic at each agentic loop step. The implementation correctly:
- Handles both workflows and regular processors
- Threads
retryCountthrough for retry awareness- Captures tripwire metadata for observability (lines 1093-1102)
- Provides comprehensive JSDoc explaining the use case
Minor improvement - Line 1076 uses
(p: any)type assertion:const systemText = (message.content.content as string | undefined) ?? - message.content.parts?.map((p: any) => (p.type === 'text' ? p.text : '')).join('\n') ?? + message.content.parts?.map(p => (p.type === 'text' ? p.text : '')).join('\n') ?? '';The type can be inferred from
message.content.partsrather than usingany.packages/core/src/loop/workflows/agentic-loop/index.ts (1)
128-145: Verify the step-finish emission logic handles all tripwire scenarios correctly.The new emission logic introduces conditional step-finish events based on tripwire status and step presence. The condition
reason !== 'tripwire' || hasStepsmeans:
- Emit for all non-tripwire cases
- Emit for tripwire only when steps exist
- Skip emission only for tripwire with no steps
Ensure this logic properly handles all edge cases, particularly around max retry scenarios, processor rejections, and downstream stream consumers that depend on step-finish events.
Additionally, the
@ts-ignoreat line 141 indicates a type mismatch that should be resolved:Apply proper typing to the payload instead of using
@ts-ignore:// Consider defining a proper type for the step-finish payload type StepFinishPayload = { output: LLMIterationData<Tools, OUTPUT>['output']; stepResult: LLMIterationData<Tools, OUTPUT>['stepResult']; metadata: LLMIterationData<Tools, OUTPUT>['metadata']; messages: LLMIterationData<Tools, OUTPUT>['messages']; }; controller.enqueue({ type: 'step-finish', runId, from: ChunkFrom.AGENT, payload: typedInputData as StepFinishPayload, });packages/core/src/workflows/workflow.test.ts (1)
17993-17997: Close the newdescribeblock consistentlyThe new
describe('Agent TripWire in Workflow', ...)block currently ends with})instead of});, whereas the rest of this file consistently terminates describes/its with a semicolon. Not a functional issue (ASI will handle it), but it will likely be auto‑fixed by Prettier.I’d let the formatter normalize this or update manually to:
- }) + });packages/core/src/processors/step-schema.ts (3)
151-167: Clarify comments for MessagePart usage and role enumThe comments here are slightly out of sync with the actual schemas:
- The text says “uses the MessagePartSchema discriminated union”, but
MessagePartSchemais a plain union of part shapes (withDataPartSchema.typebeing a refined string), not a ZoddiscriminatedUnion.- The “Key fields” section lists
role: 'user' | 'assistant' | 'system', while the schema also allows'tool'.To avoid confusion for future readers, it’s worth tightening the comments to match the implementation.
-/** - * Schema for message content in processor workflows. - * Uses the MessagePartSchema discriminated union for proper UI rendering. - */ +/** + * Schema for message content in processor workflows. + * Uses MessagePartSchema (a union of discriminated part types) for proper UI rendering. + */ @@ - * - role: 'user' | 'assistant' | 'system' - Message role + * - role: 'user' | 'assistant' | 'system' | 'tool' - Message roleAlso applies to: 174-199
130-145: Factor out shared base for message content schemas to avoid drift
MessageContentSchemaandProcessorMessageContentSchemacurrently duplicate the same field structure, with the only behavioral difference being.passthrough()on the processor variant. Refactoring to a shared base reduces the chance of them drifting out of sync.-/** - * Message content structure (MastraMessageContentV2 format) - * This is a documentation-friendly schema with properly typed parts. - */ -export const MessageContentSchema = z.object({ - /** Format version - 2 corresponds to AI SDK v4 UIMessage format */ - format: z.literal(2), - /** Array of message parts (text, images, tool calls, etc.) */ - parts: z.array(MessagePartSchema), - /** Legacy content field for backwards compatibility */ - content: z.string().optional(), - /** Additional metadata */ - metadata: z.record(z.unknown()).optional(), - /** Provider-specific metadata */ - providerMetadata: z.record(z.unknown()).optional(), -}); +/** + * Base message content schema (MastraMessageContentV2 format). + * Used by both docs-facing and processor-facing schemas. + */ +const BaseMessageContentSchema = z.object({ + /** Format version - 2 corresponds to AI SDK v4 UIMessage format */ + format: z.literal(2), + /** Array of message parts (text, images, tool calls, etc.) */ + parts: z.array(MessagePartSchema), + /** Legacy content field for backwards compatibility */ + content: z.string().optional(), + /** Additional metadata */ + metadata: z.record(z.unknown()).optional(), + /** Provider-specific metadata */ + providerMetadata: z.record(z.unknown()).optional(), +}); + +export const MessageContentSchema = BaseMessageContentSchema; @@ -export const ProcessorMessageContentSchema = z - .object({ - /** Format version - 2 corresponds to AI SDK v4 UIMessage format */ - format: z.literal(2), - /** Array of message parts (text, images, tool calls, etc.) */ - parts: z.array(MessagePartSchema), - /** Legacy content field for backwards compatibility */ - content: z.string().optional(), - /** Additional metadata */ - metadata: z.record(z.unknown()).optional(), - /** Provider-specific metadata */ - providerMetadata: z.record(z.unknown()).optional(), - }) - .passthrough(); +export const ProcessorMessageContentSchema = BaseMessageContentSchema.passthrough();Please re-run
pnpm typecheckafter applying this to ensure all imports/uses of these schemas still typecheck cleanly.Also applies to: 155-168
296-299: Encode invariants forretryCountandstepNumberas non‑negative integers
retryCount(a count of retries) andstepNumber(documented as 0‑indexed) are currently plainnumbers. Tightening them to non‑negative integers better matches the semantics and can catch accidental misuse at validation time.-/** - * Number of times processors have triggered retry for this generation. - */ -const retryCountSchema = z.number().optional(); +/** + * Number of times processors have triggered retry for this generation. + * Non-negative integer count. + */ +const retryCountSchema = z.number().int().min(0).optional(); @@ phase: z.literal('inputStep'), messages: messagesSchema, messageList: messageListSchema, - stepNumber: z.number().describe('The current step number (0-indexed)'), + stepNumber: z + .number() + .int() + .min(0) + .describe('The current step number (0-indexed)'), @@ phase: z.literal('outputStep'), messages: messagesSchema, messageList: messageListSchema, - stepNumber: z.number().describe('The current step number (0-indexed)'), + stepNumber: z + .number() + .int() + .min(0) + .describe('The current step number (0-indexed)'), @@ // Retry count - retryCount: z.number().optional(), + retryCount: retryCountSchema,After this change, please verify any existing test fixtures or mocked processor step payloads that might currently pass non-integer or negative values.
Also applies to: 321-328, 358-368, 400-424
packages/core/src/stream/types.ts (1)
29-34: Consider actually usingMastraFinishReasonor removing it.
MastraFinishReasonis defined but not referenced;FinishPayload.stepResult.reasonrepeats the raw union instead. Either switch that field (and any other extended-reason sites) toMastraFinishReasonor drop the alias to avoid dead types.Also applies to: 190-201
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (22)
client-sdks/client-js/src/types.ts(1 hunks)client-sdks/react/src/lib/ai-sdk/utils/toUIMessage.test.ts(2 hunks)client-sdks/react/src/lib/ai-sdk/utils/toUIMessage.ts(2 hunks)packages/core/src/agent/agent.ts(16 hunks)packages/core/src/agent/agent.types.ts(2 hunks)packages/core/src/agent/message-list/index.ts(2 hunks)packages/core/src/llm/model/model.loop.ts(2 hunks)packages/core/src/loop/test-utils/options.ts(8 hunks)packages/core/src/loop/types.ts(3 hunks)packages/core/src/loop/workflows/agentic-loop/index.ts(2 hunks)packages/core/src/loop/workflows/stream.ts(2 hunks)packages/core/src/processors/runner.ts(17 hunks)packages/core/src/processors/step-schema.ts(1 hunks)packages/core/src/stream/index.ts(1 hunks)packages/core/src/stream/types.ts(8 hunks)packages/core/src/workflows/default.ts(5 hunks)packages/core/src/workflows/handlers/control-flow.ts(1 hunks)packages/core/src/workflows/types.ts(8 hunks)packages/core/src/workflows/workflow.test.ts(1 hunks)packages/core/src/workflows/workflow.ts(12 hunks)packages/server/src/server/handlers/agent.test.ts(1 hunks)packages/server/src/server/handlers/agents.ts(2 hunks)
🚧 Files skipped from review as they are similar to previous changes (10)
- client-sdks/react/src/lib/ai-sdk/utils/toUIMessage.ts
- packages/core/src/llm/model/model.loop.ts
- packages/server/src/server/handlers/agents.ts
- client-sdks/client-js/src/types.ts
- client-sdks/react/src/lib/ai-sdk/utils/toUIMessage.test.ts
- packages/core/src/agent/message-list/index.ts
- packages/core/src/loop/test-utils/options.ts
- packages/core/src/loop/types.ts
- packages/server/src/server/handlers/agent.test.ts
- packages/core/src/workflows/types.ts
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Run
pnpm typecheckto validate TypeScript types across all packages
Files:
packages/core/src/loop/workflows/agentic-loop/index.tspackages/core/src/workflows/handlers/control-flow.tspackages/core/src/stream/index.tspackages/core/src/processors/runner.tspackages/core/src/agent/agent.types.tspackages/core/src/stream/types.tspackages/core/src/workflows/workflow.tspackages/core/src/loop/workflows/stream.tspackages/core/src/workflows/workflow.test.tspackages/core/src/agent/agent.tspackages/core/src/workflows/default.tspackages/core/src/processors/step-schema.ts
**/*.{ts,tsx,js,jsx,json,md}
📄 CodeRabbit inference engine (CLAUDE.md)
Run
pnpm prettier:formatto format code andpnpm formatto run linting with auto-fix across all packages
Files:
packages/core/src/loop/workflows/agentic-loop/index.tspackages/core/src/workflows/handlers/control-flow.tspackages/core/src/stream/index.tspackages/core/src/processors/runner.tspackages/core/src/agent/agent.types.tspackages/core/src/stream/types.tspackages/core/src/workflows/workflow.tspackages/core/src/loop/workflows/stream.tspackages/core/src/workflows/workflow.test.tspackages/core/src/agent/agent.tspackages/core/src/workflows/default.tspackages/core/src/processors/step-schema.ts
packages/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
packages/**/*.{ts,tsx}: All packages must use TypeScript with strict type checking enabled
Use telemetry decorators for observability across components
Files:
packages/core/src/loop/workflows/agentic-loop/index.tspackages/core/src/workflows/handlers/control-flow.tspackages/core/src/stream/index.tspackages/core/src/processors/runner.tspackages/core/src/agent/agent.types.tspackages/core/src/stream/types.tspackages/core/src/workflows/workflow.tspackages/core/src/loop/workflows/stream.tspackages/core/src/workflows/workflow.test.tspackages/core/src/agent/agent.tspackages/core/src/workflows/default.tspackages/core/src/processors/step-schema.ts
**/*.{test,spec}.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{test,spec}.{ts,tsx}: Use Vitest for testing framework in test files
Co-locate test files with source code using naming patterns like *.test.ts or *.spec.ts
Files:
packages/core/src/workflows/workflow.test.ts
🧠 Learnings (7)
📚 Learning: 2025-10-29T09:37:59.778Z
Learnt from: TheIsrael1
Repo: mastra-ai/mastra PR: 9247
File: packages/core/src/loop/network/index.ts:357-393
Timestamp: 2025-10-29T09:37:59.778Z
Learning: In the agent network routing completion stream (packages/core/src/loop/network/index.ts), when `completionStream.error` is true, the first stream should not have written any text (currentTextIdx should be 0), so the fallback reset of currentText and currentTextIdx does not cause duplication.
Applied to files:
packages/core/src/loop/workflows/agentic-loop/index.tspackages/core/src/agent/agent.types.tspackages/core/src/workflows/workflow.tspackages/core/src/loop/workflows/stream.tspackages/core/src/workflows/workflow.test.tspackages/core/src/agent/agent.ts
📚 Learning: 2025-11-24T16:42:04.244Z
Learnt from: CR
Repo: mastra-ai/mastra PR: 0
File: packages/codemod/AGENTS.md:0-0
Timestamp: 2025-11-24T16:42:04.244Z
Learning: Applies to packages/codemod/src/codemods/v1/**/*.ts : Use `context.hasChanges = true` flag and `context.messages.push()` to track and report what transformations were made in the codemod
Applied to files:
packages/core/src/workflows/workflow.ts
📚 Learning: 2025-11-24T16:42:04.244Z
Learnt from: CR
Repo: mastra-ai/mastra PR: 0
File: packages/codemod/AGENTS.md:0-0
Timestamp: 2025-11-24T16:42:04.244Z
Learning: Applies to packages/codemod/src/test/__fixtures__/**/*.ts : In output fixtures, ensure all NEGATIVE test cases remain EXACTLY IDENTICAL to their input fixture counterparts to verify the codemod only transforms intended patterns
Applied to files:
packages/core/src/workflows/workflow.test.ts
📚 Learning: 2025-11-24T16:42:04.244Z
Learnt from: CR
Repo: mastra-ai/mastra PR: 0
File: packages/codemod/AGENTS.md:0-0
Timestamp: 2025-11-24T16:42:04.244Z
Learning: Applies to packages/codemod/src/test/**/*.test.ts : Include test cases for multiple occurrences of the transformation pattern, aliased imports, type imports, and mixed imports to verify the codemod works consistently
Applied to files:
packages/core/src/workflows/workflow.test.ts
📚 Learning: 2025-11-24T16:42:04.244Z
Learnt from: CR
Repo: mastra-ai/mastra PR: 0
File: packages/codemod/AGENTS.md:0-0
Timestamp: 2025-11-24T16:42:04.244Z
Learning: Applies to packages/codemod/src/test/__fixtures__/**/*.ts : In input fixtures, include both POSITIVE test cases (patterns that should transform) and NEGATIVE test cases (unrelated code with similar names/patterns that should NOT transform)
Applied to files:
packages/core/src/workflows/workflow.test.ts
📚 Learning: 2025-11-24T16:42:04.244Z
Learnt from: CR
Repo: mastra-ai/mastra PR: 0
File: packages/codemod/AGENTS.md:0-0
Timestamp: 2025-11-24T16:42:04.244Z
Learning: Applies to packages/codemod/src/test/__fixtures__/**/*.ts : Create test fixtures by copying examples DIRECTLY from migration guides in `docs/src/content/en/guides/migrations/upgrade-to-v1/` without hallucinating or inventing changes
Applied to files:
packages/core/src/workflows/workflow.test.ts
📚 Learning: 2025-11-24T16:42:04.244Z
Learnt from: CR
Repo: mastra-ai/mastra PR: 0
File: packages/codemod/AGENTS.md:0-0
Timestamp: 2025-11-24T16:42:04.244Z
Learning: Applies to packages/codemod/src/codemods/v1/**/*.ts : When transforming TypeScript type imports and usages, check the parent node type to skip transforming identifiers that are part of import declarations to avoid transforming imports from other packages with the same type name
Applied to files:
packages/core/src/agent/agent.ts
🧬 Code graph analysis (6)
packages/core/src/processors/runner.ts (8)
packages/core/src/processors/index.ts (5)
Processor(130-184)ProcessorWorkflow(215-215)ProcessorStepOutput(286-286)isProcessorWorkflow(235-252)TripWireOptions(257-257)packages/core/src/processors/step-schema.ts (1)
ProcessorStepOutput(453-453)packages/core/src/observability/types/tracing.ts (1)
TracingContext(863-866)packages/core/src/agent/trip-wire.ts (2)
TripWire(35-45)TripWireOptions(14-25)packages/core/src/agent/message-list/index.ts (2)
MessageList(124-3400)MastraDBMessage(75-77)packages/core/src/agent/types.ts (2)
MessageList(42-42)MastraDBMessage(42-42)packages/core/src/memory/types.ts (1)
MastraDBMessage(6-6)packages/core/src/stream/types.ts (1)
ChunkType(646-646)
packages/core/src/agent/agent.types.ts (1)
packages/core/src/processors/index.ts (2)
InputProcessorOrWorkflow(220-222)OutputProcessorOrWorkflow(227-229)
packages/core/src/stream/types.ts (3)
packages/core/src/llm/model/base.types.ts (1)
ToolSet(29-29)packages/core/src/processors/index.ts (1)
OutputProcessorOrWorkflow(227-229)packages/core/src/workflows/types.ts (1)
StepResult(124-129)
packages/core/src/workflows/workflow.ts (4)
packages/core/src/processors/index.ts (4)
Processor(130-184)ProcessorStepSchema(259-259)ProcessorStepOutputSchema(261-261)ProcessorStepOutput(286-286)packages/core/src/processors/step-schema.ts (3)
ProcessorStepSchema(430-430)ProcessorStepOutputSchema(400-424)ProcessorStepOutput(453-453)packages/core/src/agent/trip-wire.ts (1)
TripWire(35-45)packages/core/src/agent/types.ts (2)
MessageList(42-42)MastraDBMessage(42-42)
packages/core/src/agent/agent.ts (3)
packages/core/src/processors/index.ts (4)
InputProcessorOrWorkflow(220-222)OutputProcessorOrWorkflow(227-229)isProcessorWorkflow(235-252)ProcessorWorkflow(215-215)packages/core/src/workflows/workflow.ts (2)
isProcessor(670-684)createWorkflow(686-702)packages/core/src/workflows/evented/workflow.ts (1)
createWorkflow(303-332)
packages/core/src/workflows/default.ts (2)
packages/core/src/workflows/types.ts (1)
StepTripwireInfo(69-74)packages/core/src/agent/trip-wire.ts (1)
TripWire(35-45)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
- GitHub Check: Socket Security: Pull Request Alerts
- GitHub Check: test (express)
- GitHub Check: Prebuild
- GitHub Check: Lint
- GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (15)
packages/core/src/workflows/default.ts (2)
1-41: TripWire / StepTripwireInfo imports are appropriate for new tripwire supportImporting
TripWireandStepTripwireInfohere makes sense given this engine now needs to recognize and serialize tripwire information at the workflow level. No issues from a dependency or typing standpoint as long aspnpm typecheckpasses for the updatedStepResult/StepFailureshapes.
375-395:fmtReturnValuetripwire handling matches the new workflow status semantics; verify all consumers handlestatus: 'tripwire'The new branch that:
- detects
lastOutput.tripwire(bothTripWireinstances and plain objects),- switches
base.statusfrom'failed'to'tripwire', and- surfaces
base.tripwireinstead ofbase.erroris consistent with the design of treating processor rejections as a distinct terminal state. Non-tripwire failures still go through
formatResultError, so legacy failure behavior is preserved.Given this is a behavior change at the top-level workflow result shape, please double-check that all downstream consumers (SDKs, UI, any custom callers) now recognize
status === 'tripwire'and prefertripwireovererrorwhen present.packages/core/src/processors/runner.ts (1)
66-69: Clean type system design for processor/workflow union.The introduction of
ProcessorOrWorkflowunion type (lines 66-69) and consistent threading ofretryCountparameter throughout the codebase provides a solid foundation for the retry mechanism. The approach:
- Uses a clear type alias for the union
- Maintains backward compatibility (retryCount defaults to 0)
- Properly extends return types to include tripwire metadata
- Enables workflows to be used anywhere processors are accepted
Also applies to: 154-154, 290-290, 511-511, 737-737
packages/core/src/stream/index.ts (1)
10-10: LGTM!The addition of
MastraFinishReasonto the exported types properly expands the public API surface to support the tripwire and finish-state handling introduced in this PR.packages/core/src/loop/workflows/agentic-loop/index.ts (1)
85-86: The finishReason cast is intentional but document the type extension more clearly.The code casts
typedInputData.stepResult?.reason(which includes 'tripwire' and 'retry' extensions for processor scenarios) toStepResult<Tools>['finishReason']from ai-v5, which only recognizes standardLanguageModelV2FinishReasonvalues. While the comment explains why the cast exists, consider clarifying that this is a deliberate extension of the ai-v5 type contract and update the comment to reference theMastraFinishReasontype defined inpackages/core/src/stream/types.ts(line 33), which documents the full set of supported finish reasons including 'tripwire' and 'retry'. This improves maintainability by making the type extension explicit.packages/core/src/workflows/workflow.test.ts (1)
17923-17931: The test condition forprocessOutputStreamis correct as written.The concern in the original review comment is based on a misunderstanding of the chunk flow. While the mocked model does emit raw chunks with a
deltafield ({ type: 'text-delta', id: '1', delta: 'This is inappropriate content' }), these chunks are transformed before reaching the processor.The flow is:
- MockLanguageModelV2 emits raw LanguageModelV2StreamPart chunks with
delta- AISDKV5LanguageModel transforms these chunks via
convertFullStreamChunkToMastra- The transformation converts
value.delta→payload.textfor text-delta chunks- The processor receives the transformed ChunkType with the correct shape:
{ type: 'text-delta', payload: { text: '...' } }Therefore, the processor's check
part?.payload?.text?.includes('inappropriate')is correct and will properly detect the inappropriate content. The test will successfully exercise the tripwire path as intended.Likely an incorrect or invalid review comment.
packages/core/src/processors/step-schema.ts (1)
1-453: Confirmpnpm typecheck,pnpm prettier:format, andpnpm formatpass on your branchPer project guidelines, this TypeScript file must pass the standard typecheck and formatting commands. Please run these from the repository root and confirm they complete without errors:
pnpm typecheck pnpm prettier:format pnpm formatpackages/core/src/agent/agent.types.ts (1)
9-9: Processor/workflow unions and maxProcessorRetries docs look consistent.Using
InputProcessorOrWorkflow[]/OutputProcessorOrWorkflow[]here matches the processor/workflow unions, and themaxProcessorRetriesJSDoc now correctly describes overriding the agent default and falling back to “no retries” when unset. No further changes needed.Also applies to: 93-101
packages/core/src/stream/types.ts (1)
328-337: Tripwire data modelling and processor output wiring look coherent.
TripwirePayload,StepTripwireData,MastraStepResult, the addedtripwirefield onLLMStepResult, the workflow-steptripwireattachment, andMastraModelOutputOptions.outputProcessors?: OutputProcessorOrWorkflow[]are all shape‑compatible with theTripWireclass (reason/retry/metadata/processorId) and should round‑trip tripwire information cleanly through streams and step results.Also applies to: 633-635, 719-742, 777-779, 704-717
packages/core/src/workflows/workflow.ts (4)
274-319: Agent step tripwire handling in workflows is correctly wired.The agent-backed step now detects
tripwirechunks in both legacy and vNext streaming, writes the chunk through (vNext), then throws aTripWirewith reason/retry/metadata/processorId. This cleanly aborts the workflow step while preserving a visible tripwire event for downstream consumers.
161-172: Processor-to-step wrapping covers all phases and result shapes consistently.The new Processor overload for
createStepplus theisProcessorbranch correctly:
- Enforces presence of
messageListfor message-based phases usingMastraError.- Auto-creates a
MessageListonly whenmessagesis an array (avoids prior type issues).- Handles all three allowed return shapes (
MessageList,MastraDBMessage[],{ messages, systemMessages }) forprocessInput,processInputStep,processOutputResult, andprocessOutputStep.- Passes through stream data/state for
processOutputStreamwhile allowing processors to drop or rewrite the part.This aligns the workflow step contract with the
Processorinterface and previously fixed result-handling bugs.Also applies to: 383-627
765-811: Workflowtypeflag is wired correctly for processor workflows.Adding
public type: WorkflowType = 'default'and initializing it from theWorkflowConfig(defaulting to'default') gives processor workflows a clear'processor'discriminator without affecting existing workflows. This plays nicely with the agent’scombineProcessorsIntoWorkflowhelper.
666-684:isProcessortype guard is robust enough for runtime discrimination.The guard checks for a string
id, excludesAgent/Toolinstances, and requires at least one processor method. That’s sufficient for safely routing objects into the Processor branch ofcreateStepand for filtering incombineProcessorsIntoWorkflow.packages/core/src/agent/agent.ts (2)
125-127: Processor/workflow composition and resolution logic are sound.The agent now:
- Stores input/output processors as
DynamicArgument<InputProcessorOrWorkflow[] | OutputProcessorOrWorkflow[]>with an optional#maxProcessorRetries.- Resolves configured + memory processors, ordering memory first for input and last for output as documented.
- Collapses multiple processors into a single processor-typed workflow via
combineProcessorsIntoWorkflow, usingProcessorStepSchemaI/O andtype: 'processor', and wrapping bareProcessorinstances withcreateStep.- Exposes configured processor workflows (excluding memory) via
getConfiguredProcessorWorkflowsfor Mastra to register centrally.This design keeps runtime wiring consistent and avoids duplicating processor/workflow handling elsewhere.
Also applies to: 244-255, 305-327, 329-392, 399-442, 447-456, 458-497
1459-1469: Tripwire capture from input/output processors is correctly shaped.
__runInputProcessorsand__runOutputProcessorsnow:
- Respect overrides, only instantiating
ProcessorRunnerwhen needed.- Catch
TripWirespecifically, convert it into a{ reason, retry, metadata, processorId }tripwirefield on the return value, and rethrow other errors.- Preserve the updated
MessageListin all cases.This matches the TripWire shape used elsewhere and gives the higher-level workflow full control over retry and surfacing.
Also applies to: 1519-1529
| try { | ||
| await this.executeWorkflowAsProcessor( | ||
| processorOrWorkflow, | ||
| { | ||
| phase: 'outputResult', | ||
| messages: processableMessages, | ||
| messageList, | ||
| retryCount, | ||
| }, | ||
| tracingContext, | ||
| requestContext, | ||
| ); | ||
|
|
||
| ctx.abort = abort; | ||
| processorSpan?.end({ output: processableMessages }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
# First, let's check the structure of the file and understand the context
wc -l packages/core/src/processors/runner.tsRepository: mastra-ai/mastra
Length of output: 103
🏁 Script executed:
# Search for all usages of executeWorkflowAsProcessor to verify result handling pattern
rg -n -A5 -B2 'executeWorkflowAsProcessor' --type tsRepository: mastra-ai/mastra
Length of output: 3791
🏁 Script executed:
# Get the specific lines mentioned in the review (177-190 and 244-270)
sed -n '163,199p' packages/core/src/processors/runner.tsRepository: mastra-ai/mastra
Length of output: 1295
🏁 Script executed:
# Also check lines 244-270 for processor handling comparison
sed -n '230,280p' packages/core/src/processors/runner.tsRepository: mastra-ai/mastra
Length of output: 1793
🏁 Script executed:
# Get the executeWorkflowAsProcessor implementation
sed -n '98,140p' packages/core/src/processors/runner.tsRepository: mastra-ai/mastra
Length of output: 1588
🏁 Script executed:
# Get context around line 327 to see how result is used
sed -n '320,350p' packages/core/src/processors/runner.tsRepository: mastra-ai/mastra
Length of output: 1126
🏁 Script executed:
# Get the full executeWorkflowAsProcessor implementation including return statement
sed -n '98,160p' packages/core/src/processors/runner.tsRepository: mastra-ai/mastra
Length of output: 2272
🏁 Script executed:
# Find the ProcessorStepOutput type definition
rg -n 'type ProcessorStepOutput\|interface ProcessorStepOutput' --type ts -A10Repository: mastra-ai/mastra
Length of output: 42
🏁 Script executed:
# Search for ProcessorStepOutput definition with different approach
rg -n 'ProcessorStepOutput' --type ts packages/ | grep -E '(type|interface)' | head -5Repository: mastra-ai/mastra
Length of output: 592
🏁 Script executed:
# Also check the actual file where it's defined
rg -n 'export (type|interface) ProcessorStepOutput' --type tsRepository: mastra-ai/mastra
Length of output: 168
🏁 Script executed:
# Look at the broader context - what does the workflow return for outputResult phase
grep -r 'phase.*outputResult' packages/core/src --include='*.ts' -B2 -A2Repository: mastra-ai/mastra
Length of output: 2997
🏁 Script executed:
# Verify what gets returned from processOutputResult in tests
rg -n 'processOutputResult' packages/core/src/workflows/processor-step.test.ts -A10 | head -40Repository: mastra-ai/mastra
Length of output: 1508
🏁 Script executed:
# Check the ProcessorStepOutputSchema to see all possible fields
sed -n '400,460p' packages/core/src/processors/step-schema.tsRepository: mastra-ai/mastra
Length of output: 1761
Workflow result handling is inconsistent - return value should be used at line 178.
The executeWorkflowAsProcessor method returns ProcessorStepOutput, which for the outputResult phase includes a messages field that may contain workflow modifications. However, at line 178, the return value is discarded via await without capturing it.
This is inconsistent with:
- Line 327 (
outputStreamphase), which correctly captures the result and usesresult.part - The regular processor handling pattern (lines 244-270), which uses returned values to update messages
The workflow at line 178 should capture and apply the returned messages:
- await this.executeWorkflowAsProcessor(
+ const workflowResult = await this.executeWorkflowAsProcessor(
processorOrWorkflow,
{
phase: 'outputResult',
messages: processableMessages,
messageList,
retryCount,
},
tracingContext,
requestContext,
);
+
+ if (workflowResult.messages) {
+ processableMessages = workflowResult.messages;
+ }
processorSpan?.end({ output: processableMessages });📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| try { | |
| await this.executeWorkflowAsProcessor( | |
| processorOrWorkflow, | |
| { | |
| phase: 'outputResult', | |
| messages: processableMessages, | |
| messageList, | |
| retryCount, | |
| }, | |
| tracingContext, | |
| requestContext, | |
| ); | |
| ctx.abort = abort; | |
| processorSpan?.end({ output: processableMessages }); | |
| try { | |
| const workflowResult = await this.executeWorkflowAsProcessor( | |
| processorOrWorkflow, | |
| { | |
| phase: 'outputResult', | |
| messages: processableMessages, | |
| messageList, | |
| retryCount, | |
| }, | |
| tracingContext, | |
| requestContext, | |
| ); | |
| if (workflowResult.messages) { | |
| processableMessages = workflowResult.messages; | |
| } | |
| processorSpan?.end({ output: processableMessages }); |
🤖 Prompt for AI Agents
In packages/core/src/processors/runner.ts around lines 177 to 190, the call to
await this.executeWorkflowAsProcessor(..., { phase: 'outputResult', messages:
processableMessages, ... }) discards its ProcessorStepOutput return value;
capture the result into a const (e.g., result) and replace processableMessages
with the returned messages if present (e.g., processableMessages =
result.messages ?? processableMessages), preserving types and handling
undefined/null safely before ending processorSpan with the updated messages;
ensure any existing retryCount or tracingContext usage remains unchanged.
| // Preserve tripwire property for proper status conversion in fmtReturnValue | ||
| execResults = { | ||
| status: 'failed', | ||
| error: hasFailed.error, | ||
| tripwire: (hasFailed as any).tripwire, | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
# First, let me inspect the control-flow.ts file around the mentioned lines
cat -n packages/core/src/workflows/handlers/control-flow.ts | sed -n '160,180p'Repository: mastra-ai/mastra
Length of output: 1004
🏁 Script executed:
# Now check executeConditional around lines 428-429
cat -n packages/core/src/workflows/handlers/control-flow.ts | sed -n '420,440p'Repository: mastra-ai/mastra
Length of output: 980
🏁 Script executed:
# Search for all failure result constructions in the file
rg -n "status:\s*['\"]failed['\"]" packages/core/src/workflows/handlers/control-flow.ts -A 2 -B 2Repository: mastra-ai/mastra
Length of output: 512
🏁 Script executed:
# Search for fmtReturnValue function to understand if tripwire is actually used
rg -n "fmtReturnValue" packages/core/src/workflows/handlers/control-flow.ts -B 2 -A 5Repository: mastra-ai/mastra
Length of output: 395
🏁 Script executed:
# Check if tripwire is used elsewhere in the codebase
rg -l "tripwire" packages/core/src/workflows/ --type tsRepository: mastra-ai/mastra
Length of output: 324
🏁 Script executed:
# Search for fmtReturnValue implementation to see if it uses tripwire
rg -n "fmtReturnValue" packages/core/src/workflows/ -B 2 -A 10 --type ts | head -80Repository: mastra-ai/mastra
Length of output: 4302
🏁 Script executed:
# Find all control flow handler functions to check consistency
rg -n "^(async\s+)?function|^const.*=\s*(async\s*)?\(" packages/core/src/workflows/handlers/control-flow.ts | head -20Repository: mastra-ai/mastra
Length of output: 42
🏁 Script executed:
# Check the function signatures and names
rg -n "^export (async )?function|^export const.*=" packages/core/src/workflows/handlers/control-flow.tsRepository: mastra-ai/mastra
Length of output: 228
🏁 Script executed:
# Search for all failure result constructions to see complete pattern
rg -n "execResults\s*=" packages/core/src/workflows/handlers/control-flow.ts -A 3 | grep -E "(execResults|status.*failed|tripwire)"Repository: mastra-ai/mastra
Length of output: 629
🏁 Script executed:
# Check tripwire type definition in types.ts
rg -n "tripwire" packages/core/src/workflows/types.ts -B 2 -A 2Repository: mastra-ai/mastra
Length of output: 799
🏁 Script executed:
# Check executeLoop failure result construction
cat -n packages/core/src/workflows/handlers/control-flow.ts | sed -n '497,650p' | grep -A 20 "hasFailed"Repository: mastra-ai/mastra
Length of output: 42
🏁 Script executed:
# Look at fmtReturnValue more carefully to see if tripwire is used
cat -n packages/core/src/workflows/default.ts | sed -n '363,410p'Repository: mastra-ai/mastra
Length of output: 2187
🏁 Script executed:
# Check if tripwire is accessed in fmtReturnValue
rg -n "tripwire" packages/core/src/workflows/default.ts -B 3 -A 3Repository: mastra-ai/mastra
Length of output: 1570
🏁 Script executed:
# Search for how the result object from handlers is used in fmtReturnValue
rg -n "lastOutput\.result\." packages/core/src/workflows/default.ts | head -20Repository: mastra-ai/mastra
Length of output: 328
🏁 Script executed:
# Check executeLoop failure result construction - search more broadly
rg -n "executeLoop" packages/core/src/workflows/handlers/control-flow.ts -A 150 | grep -A 5 "hasFailed\|status.*failed" | head -40Repository: mastra-ai/mastra
Length of output: 42
🏁 Script executed:
# Simpler approach - get executeLoop function and look for all execResults assignments
sed -n '497,690p' packages/core/src/workflows/handlers/control-flow.ts | grep -n "execResults\|status.*failed" -A 3Repository: mastra-ai/mastra
Length of output: 42
🏁 Script executed:
# Check executeForeach for failure handling
sed -n '690,900p' packages/core/src/workflows/handlers/control-flow.ts | grep -n "execResults\|hasFailed\|status.*failed" -B 2 -A 3Repository: mastra-ai/mastra
Length of output: 659
🏁 Script executed:
# Read executeLoop function directly to see failure handling
sed -n '497,689p' packages/core/src/workflows/handlers/control-flow.tsRepository: mastra-ai/mastra
Length of output: 5268
Add tripwire preservation to executeConditional and executeForeach for consistent status conversion.
The executeParallel function preserves the tripwire property when handling failures, enabling fmtReturnValue to properly convert failed results to 'tripwire' status. However, executeConditional (line 429) and executeForeach (line 814) do not preserve it, causing tripwire data to be lost and failures to be reported as 'failed' instead of 'tripwire'.
Apply these fixes:
executeConditional (line 429):
if (hasFailed) {
- execResults = { status: 'failed', error: hasFailed.error };
+ execResults = {
+ status: 'failed',
+ error: hasFailed.error,
+ tripwire: (hasFailed as any).tripwire,
+ };
}executeForeach (line 814):
- const execResults = { status, error, suspendPayload, suspendedAt, endedAt, output };
+ const execResults = { status, error, suspendPayload, suspendedAt, endedAt, output, tripwire };Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
packages/core/src/workflows/handlers/control-flow.ts around lines 168-173 (and
similarly at ~429 and ~814): the failure branches in executeConditional and
executeForeach are not preserving the tripwire property, causing fmtReturnValue
to misclassify tripwire failures as plain 'failed'; update the failure result
construction so it mirrors executeParallel by adding tripwire: (hasFailed as
any).tripwire to the execResults object whenever you set status: 'failed' and
include error: hasFailed.error.
| execResults = { | ||
| status: 'failed', | ||
| error: hasFailed.error, | ||
| tripwire: (hasFailed as any).tripwire, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Type cast to any undermines type safety.
Using (hasFailed as any).tripwire bypasses TypeScript's strict type checking. If tripwire is a valid property that should be propagated from failed step results, it should be properly typed in the StepFailure interface rather than accessed through a type cast.
Consider updating the type definition to include the tripwire property:
- tripwire: (hasFailed as any).tripwire,
+ tripwire: hasFailed.tripwire,And ensure the StepFailure type in the workflow types includes the tripwire property with the correct type signature (based on the AI summary, this should be an object with reason, retry, metadata, and processorId fields).
Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In packages/core/src/workflows/handlers/control-flow.ts around line 172, the
code uses a type cast `(hasFailed as any).tripwire` which bypasses TypeScript
safety; update the StepFailure/failed-step result type to include a properly
typed `tripwire` property (an object with fields: reason: string, retry: boolean
or number as appropriate, metadata: Record<string, unknown>, and processorId:
string), replace the any cast by referencing the typed property (e.g.,
hasFailed.tripwire), and adjust any upstream type imports/usage so the
`tripwire` field is carried through the failure shape instead of being accessed
via `any`.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
♻️ Duplicate comments (3)
packages/core/src/loop/workflows/stream.ts (1)
193-194: Type suppression improved but underlying issue remains.The change from
@ts-ignoreto@ts-expect-erroris a good practice improvement (it will error if the type issue is ever fixed), and the comment is more informative. However, the core recommendation from the previous review still applies: the type definitions should be updated to properly reflect thatstepResult.reasoncan be'tripwire' | 'retry'in addition to other values.Please run the following script to verify whether the type definitions have been updated:
#!/bin/bash # Description: Check if stepResult.reason type includes 'tripwire' and 'retry' as valid values # Search for StepResult type definitions echo "=== Searching for StepResult type definitions ===" rg -nP --type=ts -C5 'type\s+StepResult|interface\s+StepResult' # Search for reason field definitions in step-related types echo -e "\n=== Searching for reason field definitions ===" rg -nP --type=ts -C3 '\breason\s*[?:].*\b(FinishReason|string)' # Check for any recent additions of 'tripwire' or 'retry' to type unions echo -e "\n=== Checking for tripwire/retry in type definitions ===" rg -nP --type=ts "'tripwire'|'retry'" -g '**/types.ts' -g '**/loop/**/*.ts'As per the previous review comment and coding guidelines requiring strict TypeScript type checking, consider updating the type definitions to include
'tripwire' | 'retry'as valid values forstepResult.reason.packages/core/src/workflows/workflow.test.ts (1)
17898-17905: Align streaming test with vNext event type and remove unused chunk collectionIn the streaming tripwire test you’re still collecting
streamVNextoutput into aStreamEvent[], but never assert onchunks:// Use streaming to verify workflow returns tripwire status const chunks: StreamEvent[] = []; const streamResult = run.streamVNext({ inputData: { prompt: 'This has forbidden content' } }); // Collect all chunks for await (const chunk of streamResult.fullStream) { chunks.push(chunk); }
streamVNextproduces vNext workflow events (e.g.WorkflowStreamEvent/ChunkType), not legacyStreamEvent, and sincechunksis unused this also adds noise to the test. You can both fix the type mismatch and simplify by just draining the stream:- // Use streaming to verify workflow returns tripwire status - const chunks: StreamEvent[] = []; - const streamResult = run.streamVNext({ inputData: { prompt: 'This has forbidden content' } }); - - // Collect all chunks - for await (const chunk of streamResult.fullStream) { - chunks.push(chunk); - } + // Use streaming to verify workflow returns tripwire status + const streamResult = run.streamVNext({ inputData: { prompt: 'This has forbidden content' } }); + + // Drain the stream + for await (const _chunk of streamResult.fullStream) { + // no-op + }This matches the vNext API and keeps the test focused on the
result.status === 'tripwire'assertions.packages/core/src/agent/agent.ts (1)
2972-2990: Add explicit tripwire status handling in generate/stream result processing.Both
generate(lines 2972-2990) andstream(lines 3051-3069) only check for'success'and'failed'status. If the underlying workflow returnsstatus === 'tripwire'(as enabled byDefaultExecutionEngine.fmtReturnValue), these methods fall into the generic "unknown error" case, losing the structured tripwire payload (reason, retry, metadata, processorId).Add an explicit tripwire branch before the generic error case in both methods:
if (result.status !== 'success') { + if (result.status === 'tripwire') { + const tripwireResult = result as { + tripwire?: { reason?: string; retry?: boolean; metadata?: unknown; processorId?: string } + }; + throw new TripWire( + tripwireResult.tripwire?.reason ?? 'Processor tripwire triggered', + { + retry: tripwireResult.tripwire?.retry, + metadata: tripwireResult.tripwire?.metadata + }, + tripwireResult.tripwire?.processorId, + ); + } if (result.status === 'failed') { throw new MastraError(This ensures callers, server layers, and UI can reliably detect and surface tripwire events with full context.
Note: This issue was previously flagged in past review comments but remains unaddressed.
Also applies to: 3051-3069
🧹 Nitpick comments (8)
client-sdks/ai-sdk/src/helpers.ts (1)
60-66: Consider defensive coding for consistency.The
finishcase accesseschunk.payload.stepResult.reasondirectly, whilemiddleware.tsuses optional chaining with a fallback (stepResult?.reason || 'stop'). If there's any scenario wherestepResultcould be undefined in this conversion context, this could throw at runtime.If the type guarantees
stepResultis always defined here, this is fine. Otherwise, consider aligning with the middleware pattern:case 'finish': { return { type: 'finish', - finishReason: toAISDKFinishReason(chunk.payload.stepResult.reason), + finishReason: toAISDKFinishReason(chunk.payload.stepResult?.reason || 'stop'), totalUsage: chunk.payload.output.usage, }; }packages/core/src/agent/agent.ts (1)
329-392: Consider defensive copying to avoid workflow mutation side effects.Lines 350 and 367 directly mutate the input workflow by setting
workflow.type = 'processor'. While the comment at lines 347-349 states this is intentional, mutation can cause subtle bugs if the same workflow instance is referenced elsewhere.Consider creating a shallow copy or using a wrapper approach:
if (processors.length === 1 && isProcessorWorkflow(processors[0]!)) { const workflow = processors[0]!; - if (!workflow.type) { - workflow.type = 'processor'; - } - return [workflow]; + // Return workflow with type set, using shallow copy to avoid mutation + return [Object.assign(Object.create(Object.getPrototypeOf(workflow)), workflow, { + type: workflow.type || 'processor' + }) as T]; }Alternatively, if the Workflow class supports it, add a dedicated
.asProcessor()method that returns a properly marked instance without mutating the original.packages/core/src/loop/workflows/agentic-loop/index.ts (1)
85-86: ThefinishReasoncast is a necessary workaround for upstream type constraintsThe cast to
StepResult<Tools>['finishReason']is required becauseLLMIterationStepResult.reasonextends the base finish reasons with'tripwire' | 'retry'(defined inpackages/core/src/loop/workflows/schema.ts), but the upstreamStepResulttype does not include these processor-specific values. As a longer-term improvement, updating the coreStepResulttype definition to explicitly include these values would eliminate the cast and the'unknown'fallback, providing stronger compile-time guarantees and removing the type friction.packages/core/src/stream/types.ts (5)
16-16: Type-only import ofOutputProcessorOrWorkflowis appropriateUsing
import typehere keeps this module purely type-level and avoids runtime cycles, which is ideal given it’s a shared types file. No changes needed; just ensure any future need for strongly typed tripwire metadata is handled by threading the generic parameter through the options rather than widening toany.
29-34: ReuseMastraFinishReasonwhere the extended finish reasons are usedYou define
MastraFinishReasonbut still inline the same union inFinishPayload.stepResult.reason. Reusing the alias keeps the union in one place and avoids drift if you later add more Mastra-specific reasons.export type MastraFinishReason = LanguageModelV2FinishReason | 'tripwire' | 'retry'; interface FinishPayload<Tools extends ToolSet = ToolSet> { stepResult: { - /** Includes 'tripwire' and 'retry' for processor scenarios */ - reason: LanguageModelV2FinishReason | 'tripwire' | 'retry'; + /** Includes 'tripwire' and 'retry' for processor scenarios */ + reason: MastraFinishReason; };If
MastraFinishReasonis not used anywhere else yet, this also guarantees it stays in sync with the actual payload shape.Also applies to: 190-194
200-201: Tripwire-awareMastraStepResultwiring looks consistent and non-breakingSwitching
output.stepsin bothFinishPayloadandStepFinishPayloadtoMastraStepResult<Tools>[]and threadingStepTripwireDataintoWorkflowStreamEventandLLMStepResultgives you a single, backward-compatible extension point for per-step tripwire data while preserving the underlying AI SDKStepResultvia intersection. This should make it straightforward for callers (and the UI) to detect processor rejections at both the streaming and workflow layers without breaking existing code that only understands the baseStepResultshape.Also applies to: 255-256, 633-635, 719-741, 777-778
328-337: AlignTripwirePayloadandStepTripwireDatato avoid type drift
TripwirePayload<TMetadata>andStepTripwireDatacurrently duplicate the same shape, but onlyTripwirePayloadis generic over metadata. To reduce maintenance and keep the two in sync, consider makingStepTripwireDataan alias ofTripwirePayload(with a default for metadata):-interface TripwirePayload<TMetadata = unknown> { +interface TripwirePayload<TMetadata = unknown> { /** The reason for the tripwire */ reason: string; /** If true, the agent should retry with the tripwire reason as feedback */ retry?: boolean; /** Strongly typed metadata from the processor */ metadata?: TMetadata; /** The ID of the processor that triggered the tripwire */ processorId?: string; } /** * Tripwire data attached to a step when a processor triggers a tripwire. * When a step has tripwire data, its text is excluded from the final output. */ -export interface StepTripwireData { - /** The tripwire reason */ - reason: string; - /** Whether retry was requested */ - retry?: boolean; - /** Additional metadata from the tripwire */ - metadata?: unknown; - /** ID of the processor that triggered the tripwire */ - processorId?: string; -} +export type StepTripwireData<TMetadata = unknown> = TripwirePayload<TMetadata>;Existing uses without a type argument will continue to default to
unknown, but this opens the door to threading typed metadata end-to-end later without another type change.Also applies to: 719-731
711-711:outputProcessorsnow accepting workflows matches the new orchestration modelChanging
outputProcessorstoOutputProcessorOrWorkflow[]correctly reflects that callers can now pass either processors or processor workflows. This keeps the signature aligned with the new composition API; if you later want fully typed tripwire metadata, you can consider generic parameters onMastraModelOutputOptions(e.g.,OutputProcessorOrWorkflow<TTripwireMetadata>[]) and thread that through, but it’s reasonable to keep it at the defaultunknownfor now. Also, per the repo guidelines, please ensurepnpm typecheck,pnpm prettier:format, andpnpm formathave been run after these TypeScript changes.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (24)
client-sdks/ai-sdk/src/helpers.ts(4 hunks)client-sdks/ai-sdk/src/middleware.ts(2 hunks)client-sdks/client-js/src/types.ts(1 hunks)client-sdks/react/src/lib/ai-sdk/utils/toUIMessage.test.ts(2 hunks)client-sdks/react/src/lib/ai-sdk/utils/toUIMessage.ts(2 hunks)packages/core/src/agent/agent.ts(16 hunks)packages/core/src/agent/agent.types.ts(2 hunks)packages/core/src/agent/message-list/index.ts(2 hunks)packages/core/src/llm/model/model.loop.ts(2 hunks)packages/core/src/loop/test-utils/options.ts(8 hunks)packages/core/src/loop/types.ts(3 hunks)packages/core/src/loop/workflows/agentic-loop/index.ts(2 hunks)packages/core/src/loop/workflows/stream.ts(2 hunks)packages/core/src/processors/runner.ts(17 hunks)packages/core/src/processors/step-schema.ts(1 hunks)packages/core/src/stream/index.ts(1 hunks)packages/core/src/stream/types.ts(8 hunks)packages/core/src/workflows/default.ts(5 hunks)packages/core/src/workflows/handlers/control-flow.ts(1 hunks)packages/core/src/workflows/types.ts(8 hunks)packages/core/src/workflows/workflow.test.ts(1 hunks)packages/core/src/workflows/workflow.ts(12 hunks)packages/server/src/server/handlers/agent.test.ts(1 hunks)packages/server/src/server/handlers/agents.ts(2 hunks)
🚧 Files skipped from review as they are similar to previous changes (9)
- packages/core/src/llm/model/model.loop.ts
- client-sdks/client-js/src/types.ts
- packages/core/src/workflows/handlers/control-flow.ts
- packages/server/src/server/handlers/agent.test.ts
- packages/server/src/server/handlers/agents.ts
- packages/core/src/stream/index.ts
- client-sdks/react/src/lib/ai-sdk/utils/toUIMessage.test.ts
- packages/core/src/agent/message-list/index.ts
- packages/core/src/loop/types.ts
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Run
pnpm typecheckto validate TypeScript types across all packages
Files:
packages/core/src/loop/workflows/stream.tspackages/core/src/loop/workflows/agentic-loop/index.tsclient-sdks/react/src/lib/ai-sdk/utils/toUIMessage.tspackages/core/src/loop/test-utils/options.tsclient-sdks/ai-sdk/src/middleware.tspackages/core/src/agent/agent.types.tspackages/core/src/workflows/types.tspackages/core/src/workflows/default.tspackages/core/src/processors/runner.tsclient-sdks/ai-sdk/src/helpers.tspackages/core/src/workflows/workflow.test.tspackages/core/src/stream/types.tspackages/core/src/agent/agent.tspackages/core/src/workflows/workflow.tspackages/core/src/processors/step-schema.ts
**/*.{ts,tsx,js,jsx,json,md}
📄 CodeRabbit inference engine (CLAUDE.md)
Run
pnpm prettier:formatto format code andpnpm formatto run linting with auto-fix across all packages
Files:
packages/core/src/loop/workflows/stream.tspackages/core/src/loop/workflows/agentic-loop/index.tsclient-sdks/react/src/lib/ai-sdk/utils/toUIMessage.tspackages/core/src/loop/test-utils/options.tsclient-sdks/ai-sdk/src/middleware.tspackages/core/src/agent/agent.types.tspackages/core/src/workflows/types.tspackages/core/src/workflows/default.tspackages/core/src/processors/runner.tsclient-sdks/ai-sdk/src/helpers.tspackages/core/src/workflows/workflow.test.tspackages/core/src/stream/types.tspackages/core/src/agent/agent.tspackages/core/src/workflows/workflow.tspackages/core/src/processors/step-schema.ts
packages/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
packages/**/*.{ts,tsx}: All packages must use TypeScript with strict type checking enabled
Use telemetry decorators for observability across components
Files:
packages/core/src/loop/workflows/stream.tspackages/core/src/loop/workflows/agentic-loop/index.tspackages/core/src/loop/test-utils/options.tspackages/core/src/agent/agent.types.tspackages/core/src/workflows/types.tspackages/core/src/workflows/default.tspackages/core/src/processors/runner.tspackages/core/src/workflows/workflow.test.tspackages/core/src/stream/types.tspackages/core/src/agent/agent.tspackages/core/src/workflows/workflow.tspackages/core/src/processors/step-schema.ts
**/*.{test,spec}.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{test,spec}.{ts,tsx}: Use Vitest for testing framework in test files
Co-locate test files with source code using naming patterns like *.test.ts or *.spec.ts
Files:
packages/core/src/workflows/workflow.test.ts
🧠 Learnings (8)
📚 Learning: 2025-10-29T09:37:59.778Z
Learnt from: TheIsrael1
Repo: mastra-ai/mastra PR: 9247
File: packages/core/src/loop/network/index.ts:357-393
Timestamp: 2025-10-29T09:37:59.778Z
Learning: In the agent network routing completion stream (packages/core/src/loop/network/index.ts), when `completionStream.error` is true, the first stream should not have written any text (currentTextIdx should be 0), so the fallback reset of currentText and currentTextIdx does not cause duplication.
Applied to files:
packages/core/src/loop/workflows/stream.tspackages/core/src/loop/workflows/agentic-loop/index.tspackages/core/src/agent/agent.types.tsclient-sdks/ai-sdk/src/helpers.tspackages/core/src/workflows/workflow.test.tspackages/core/src/agent/agent.tspackages/core/src/workflows/workflow.ts
📚 Learning: 2025-11-24T16:42:04.244Z
Learnt from: CR
Repo: mastra-ai/mastra PR: 0
File: packages/codemod/AGENTS.md:0-0
Timestamp: 2025-11-24T16:42:04.244Z
Learning: Applies to packages/codemod/src/test/__fixtures__/**/*.ts : In output fixtures, ensure all NEGATIVE test cases remain EXACTLY IDENTICAL to their input fixture counterparts to verify the codemod only transforms intended patterns
Applied to files:
packages/core/src/loop/test-utils/options.tspackages/core/src/workflows/workflow.test.ts
📚 Learning: 2025-11-24T16:42:04.244Z
Learnt from: CR
Repo: mastra-ai/mastra PR: 0
File: packages/codemod/AGENTS.md:0-0
Timestamp: 2025-11-24T16:42:04.244Z
Learning: Applies to packages/codemod/src/test/__fixtures__/**/*.ts : Create test fixtures by copying examples DIRECTLY from migration guides in `docs/src/content/en/guides/migrations/upgrade-to-v1/` without hallucinating or inventing changes
Applied to files:
packages/core/src/loop/test-utils/options.tspackages/core/src/workflows/workflow.test.ts
📚 Learning: 2025-11-24T16:42:04.244Z
Learnt from: CR
Repo: mastra-ai/mastra PR: 0
File: packages/codemod/AGENTS.md:0-0
Timestamp: 2025-11-24T16:42:04.244Z
Learning: Applies to packages/codemod/src/test/**/*.test.ts : Include test cases for multiple occurrences of the transformation pattern, aliased imports, type imports, and mixed imports to verify the codemod works consistently
Applied to files:
packages/core/src/loop/test-utils/options.tspackages/core/src/workflows/workflow.test.ts
📚 Learning: 2025-11-24T16:42:04.244Z
Learnt from: CR
Repo: mastra-ai/mastra PR: 0
File: packages/codemod/AGENTS.md:0-0
Timestamp: 2025-11-24T16:42:04.244Z
Learning: Applies to packages/codemod/src/test/__fixtures__/**/*.ts : In input fixtures, include both POSITIVE test cases (patterns that should transform) and NEGATIVE test cases (unrelated code with similar names/patterns that should NOT transform)
Applied to files:
packages/core/src/loop/test-utils/options.tspackages/core/src/workflows/workflow.test.ts
📚 Learning: 2025-11-24T16:42:04.244Z
Learnt from: CR
Repo: mastra-ai/mastra PR: 0
File: packages/codemod/AGENTS.md:0-0
Timestamp: 2025-11-24T16:42:04.244Z
Learning: Applies to packages/codemod/src/test/**/*.test.ts : Do NOT use `UPDATE_SNAPSHOT` to force tests to pass; instead fix the codemod implementation when tests fail
Applied to files:
packages/core/src/loop/test-utils/options.ts
📚 Learning: 2025-11-24T16:42:04.244Z
Learnt from: CR
Repo: mastra-ai/mastra PR: 0
File: packages/codemod/AGENTS.md:0-0
Timestamp: 2025-11-24T16:42:04.244Z
Learning: Applies to packages/codemod/src/codemods/v1/**/*.ts : When transforming TypeScript type imports and usages, check the parent node type to skip transforming identifiers that are part of import declarations to avoid transforming imports from other packages with the same type name
Applied to files:
packages/core/src/agent/agent.ts
📚 Learning: 2025-11-24T16:42:04.244Z
Learnt from: CR
Repo: mastra-ai/mastra PR: 0
File: packages/codemod/AGENTS.md:0-0
Timestamp: 2025-11-24T16:42:04.244Z
Learning: Applies to packages/codemod/src/codemods/v1/**/*.ts : Use `context.hasChanges = true` flag and `context.messages.push()` to track and report what transformations were made in the codemod
Applied to files:
packages/core/src/workflows/workflow.ts
🧬 Code graph analysis (10)
client-sdks/react/src/lib/ai-sdk/utils/toUIMessage.ts (1)
client-sdks/react/src/lib/ai-sdk/types.ts (1)
MastraUIMessage(42-42)
client-sdks/ai-sdk/src/middleware.ts (1)
client-sdks/ai-sdk/src/helpers.ts (1)
toAISDKFinishReason(18-23)
packages/core/src/agent/agent.types.ts (1)
packages/core/src/processors/index.ts (2)
InputProcessorOrWorkflow(220-222)OutputProcessorOrWorkflow(227-229)
packages/core/src/workflows/types.ts (1)
packages/core/src/observability/types/tracing.ts (1)
TracingProperties(871-874)
packages/core/src/workflows/default.ts (2)
packages/core/src/workflows/types.ts (1)
StepTripwireInfo(69-74)packages/core/src/agent/trip-wire.ts (1)
TripWire(35-45)
client-sdks/ai-sdk/src/helpers.ts (3)
packages/core/src/stream/index.ts (1)
MastraFinishReason(10-10)packages/core/src/stream/types.ts (1)
MastraFinishReason(33-33)packages/core/src/stream/base/output.ts (1)
chunk(1242-1245)
packages/core/src/workflows/workflow.test.ts (2)
packages/core/src/workflows/workflow.ts (2)
createWorkflow(686-702)createStep(174-645)packages/core/src/workflows/evented/workflow.ts (2)
createWorkflow(303-332)createStep(135-301)
packages/core/src/stream/types.ts (4)
packages/core/src/llm/model/base.types.ts (1)
ToolSet(29-29)packages/core/src/processors/index.ts (1)
OutputProcessorOrWorkflow(227-229)packages/core/src/observability/types/tracing.ts (1)
TracingContext(863-866)packages/core/src/workflows/types.ts (1)
StepResult(124-129)
packages/core/src/agent/agent.ts (3)
packages/core/src/processors/index.ts (5)
InputProcessorOrWorkflow(220-222)OutputProcessorOrWorkflow(227-229)isProcessorWorkflow(235-252)ProcessorStepSchema(259-259)ProcessorWorkflow(215-215)packages/core/src/workflows/workflow.ts (2)
isProcessor(670-684)createWorkflow(686-702)packages/core/src/processors/step-schema.ts (1)
ProcessorStepSchema(430-430)
packages/core/src/processors/step-schema.ts (1)
packages/core/src/agent/types.ts (2)
MastraMessageContentV2(42-42)MessageList(42-42)
🔇 Additional comments (35)
packages/core/src/loop/workflows/stream.ts (1)
182-184: LGTM! Improved stream completion semantics.The updated comments clearly document the important behavioral change to always emit a finish chunk, even for tripwire cases. This ensures proper stream completion and promise resolution, which aligns with the broader tripwire improvements in this PR.
client-sdks/ai-sdk/src/middleware.ts (1)
24-24: LGTM! Proper integration of finish reason mapping.The import and usage of
toAISDKFinishReasoncorrectly maps Mastra's extended finish reasons ('tripwire', 'retry') to AI SDK-compatible values. The defensive coding with optional chaining and fallback to'stop'ensures robustness whenstepResultis undefined.Also applies to: 741-741
client-sdks/ai-sdk/src/helpers.ts (2)
13-23: Well-designed helper for finish reason normalization.The
toAISDKFinishReasonhelper cleanly addresses the mapping of Mastra-specific finish reasons to AI SDK-compatible values. The documentation is clear about the purpose and the implementation is straightforward.
263-272: Tripwire payload restructuring looks correct.The updated payload structure properly exposes the new tripwire API fields (
reason,retry,metadata,processorId) to downstream consumers. This aligns well with the PR's extended tripwire API objectives.packages/core/src/loop/test-utils/options.ts (3)
1667-1667: LGTM! Tripwire field properly threaded through test expectations.The explicit
"tripwire": undefinedadditions ensure the tripwire property is consistently present in step result snapshots, aligning with the new structured tripwire object introduced in this PR.Also applies to: 1727-1727, 1814-1814, 1874-1874
6840-6848: Verify token accumulation for multi-step abort.The finish event shows
totalUsagewith accumulated tokens (inputTokens: 3, outputTokens: 10, totalTokens: 13) from the first step before the abort in the second step. Confirm this matches the expected behavior where partial progress is preserved.The token usage matches the
testUsagefrom line 6619, suggesting the first step's usage is correctly preserved. Verify this aligns with the PR's goal of preserving rejected steps atresult.steps[n].tripwire.
6527-6535: Clarify whether user-initiated abort should emit finishReason "tripwire".The test shows a user abort (AbortError from the browser API) emitting
finishReason: "tripwire"with zero usage, but the type definition explicitly documents "tripwire" as for "processor scenarios." This semantic mismatch needs resolution: either the "tripwire" finish reason scope should be expanded to include user aborts, or the user abort should map to a standardLanguageModelV2FinishReasonvalue instead.packages/core/src/processors/step-schema.ts (1)
1-453: LGTM! Well-structured schema definitions for processor workflows.The file provides comprehensive Zod schemas with proper discriminated unions, clear documentation, and appropriate use of passthrough() for extensibility. The phase-based discriminated union (lines 385-391) is particularly well-designed for type inference.
packages/core/src/agent/agent.types.ts (2)
9-9: LGTM! Type updates align with processor-workflow unification.The import and field type changes properly support the new InputProcessorOrWorkflow/OutputProcessorOrWorkflow union types, enabling processors and workflows to be used interchangeably.
Also applies to: 93-95
96-101: LGTM! JSDoc correctly describes retry defaults.The documentation now accurately reflects that retries are opt-in, defaulting to the agent's
maxProcessorRetrieswhen unset. This addresses the previous review concern about unclear default behavior.packages/core/src/workflows/default.ts (2)
277-338: LGTM! Proper TripWire serialization for workflow results.The
executeStepWithRetrymethod correctly extracts TripWire instance data into a plain object at the final retry, ensuring tripwire metadata survives serialization. The return type union properly captures both success and error cases with optional tripwire data.
378-395: LGTM! Dual tripwire format handling for flexibility.The
fmtReturnValuemethod properly distinguishes between TripWire instances and serialized tripwire objects, setting the workflow status to 'tripwire' instead of 'failed'. This enables UI and downstream code to differentiate processor rejections from actual failures.packages/core/src/processors/runner.ts (3)
94-148: LGTM! Workflow-as-processor execution with proper error handling.The
executeWorkflowAsProcessormethod correctly:
- Executes workflows using the standard workflow API
- Distinguishes 'tripwire' status from 'failed' status
- Re-throws TripWire errors for proper agent handling
- Handles missing output gracefully by returning input unchanged
The type assertion at lines 114-116 uses explicit type definition rather than
any, which was addressed in previous review iterations.
150-279: LGTM! Unified output processor handling for workflows and processors.The
runOutputProcessorsmethod cleanly separates workflow and processor execution paths while maintaining consistent:
- Span creation for observability
- TripWire error propagation
- Processor/workflow type discrimination
The workflow branch correctly passes
phase: 'outputResult'and all necessary context.
908-1111: LGTM! New per-step output processing enables retry-capable guardrails.The
runProcessOutputStepmethod introduces a new execution hook that runs after each LLM response but before tool execution. This is ideal for implementing guardrails that can trigger retries by callingabort(reason, { retry: true }).Key features:
- Passes rich context: stepNumber, finishReason, toolCalls, text
- Supports both workflow and processor implementations
- Properly handles TripWire errors with retry metadata
- Consistent span creation and mutation tracking
packages/core/src/agent/agent.ts (3)
463-497: LGTM! Processor workflow exposure for Mastra registration.The
getConfiguredProcessorWorkflowsmethod correctly:
- Excludes memory-derived processors to avoid triggering factory functions
- Handles both static and function-based processor configs
- Combines processors into workflows for consistent handling
- Filters results to return only workflow instances
This enables Mastra to auto-register processor workflows for the agent.
1462-1503: LGTM! Processor methods now return structured tripwire data.Both
__runInputProcessorsand__runOutputProcessorscorrectly:
- Catch TripWire exceptions from processor/workflow execution
- Extract structured tripwire data (reason, retry, metadata, processorId)
- Return tripwire alongside messageList for upstream handling
- Preserve other errors as MastraError
This enables the retry mechanism and proper tripwire event propagation to the UI.
Also applies to: 1510-1558
2926-3003: LGTM! Centralized processor retry configuration.Both
generateandstreammethods correctly passmaxProcessorRetriesto execution workflow (lines 2967, 3046), with proper precedence: options override > agent default. This enables per-execution and per-agent retry control.Also applies to: 3005-3072
packages/core/src/workflows/workflow.ts (6)
4-4: LGTM! Necessary imports for processor integration.All required types and utilities are imported to support processor-as-step functionality, tripwire handling, and message processing.
Also applies to: 8-10, 19-21, 25-25, 53-53
274-318: Excellent tripwire handling across streaming paths.The implementation correctly detects tripwire chunks in both legacy and vNext streaming paths, halts stream processing, and throws a structured
TripWireerror with all necessary payload details (reason, retry options, metadata, and processorId).
161-172: Processor overload correctly typed.The overload properly uses
ProcessorStepOutputSchemafor the output schema, ensuring TypeScript inference aligns with the actual execute function return type.
383-631: Comprehensive and well-structured processor-as-step implementation.This processor integration is excellent:
- Phase-based execution: Cleanly separates concerns across input, inputStep, outputStream, outputResult, and outputStep phases
- Consistent result normalization: All message-handling phases properly handle the three return types (MessageList, array, object with messages/systemMessages)
- Defensive programming: Required messageList presence is validated with clear error messages
- Proper data flow: The passThrough object enables processor workflow chaining
- Safe auto-creation: MessageList is auto-created from messages only when
Array.isArray(messages)is true- TripWire propagation: Intentionally allows TripWire errors to bubble up and halt workflow execution
All previously identified issues have been resolved. The implementation follows best practices and maintains type safety through schema validation.
666-684: Robust type guard for processor detection.The
isProcessorfunction is well-designed:
- Validates object structure and id property
- Explicitly excludes Agent and Tool to prevent false positives
- Requires at least one processor method to be present
- Properly exported for use throughout the codebase
765-766: Clean addition of workflow type discriminator.The
typefield enables distinguishing processor workflows from default workflows. The implementation follows TypeScript best practices with proper initialization and documentation.Also applies to: 795-795, 810-810
client-sdks/react/src/lib/ai-sdk/utils/toUIMessage.ts (1)
150-173: Code looks good. All accessed fields (reason, retry, metadata, processorId) are present in the TripwirePayload type definition, with reason as a required field and the others as optional.packages/core/src/loop/workflows/agentic-loop/index.ts (2)
128-145: Tripwire emission rule looks right; verifyoutput.stepsis always populated for “steps + tripwire” casesThe
shouldEmitStepFinishcondition (“emit unlessreason === 'tripwire'and there are zerooutput.steps”) matches the intended behavior: nostep-finishwhen a pure tripwire occurs before any steps, but still emitting when tripwire happens after one or more steps (e.g., max retries exceeded). This relies ontypedInputData.output.stepsbeing the canonical list of executed steps in all retry/tripwire paths; if any processor/workflow path can accumulate steps without populatingoutput.steps, we’d incorrectly suppressstep-finishin those cases.To double‑check that assumption across the codebase:
#!/bin/bash set -euo pipefail # Find where `output.steps` is written to or shaped for LLMIterationData rg -n "output\.steps" --type ts -C4 # Inspect LLMIterationData and related shaping logic ast-grep --pattern $'type LLMIterationData<$$_> = $_' || true ast-grep --pattern $'interface LLMIterationData<$$_> $_' || true
1-156: Run TypeScript typecheck and format/lint for this changeSince this is a
packages/coreTypeScript file, please ensure it passes strict type checking and project formatting/linting (pnpm typecheck,pnpm prettier:format,pnpm format) before merging to catch any subtle typing or style regressions introduced by the new tripwire logic.packages/core/src/workflows/types.ts (8)
21-26: LGTM! Clean type definition with clear documentation.The
WorkflowTypediscriminator is well-defined with appropriate JSDoc explaining the distinction between default and processor workflows.
68-74: Well-designed tripwire interface.The
StepTripwireInfointerface appropriately balances required vs optional fields. Thereasonis required (essential context), whileretry,metadata, andprocessorIdare optional. The flexibleRecord<string, unknown>type for metadata accommodates dynamic processor data.
88-89: LGTM! Consistent integration with StepFailure.The optional
tripwirefield is appropriately added toStepFailure, with clear documentation indicating it's present when a step fails due to processor rejection.
219-219: LGTM! Appropriate status extension.The
'tripwire'status is correctly added to theWorkflowRunStatusunion, enabling workflows to distinguish processor rejections from other failure states.
285-286: LGTM! Properly typed state extension.The optional
tripwirefield onWorkflowRunStateis correctly typed and documented. While TypeScript doesn't enforce thattripwireis present whenstatusis'tripwire', this is a runtime concern that implementations must handle.
308-309: LGTM! Clear processor workflow indicator.The
isProcessorWorkflowflag provides a straightforward way to identify processor-generated workflows, with clear documentation about its purpose.
562-563: LGTM! Consistent workflow configuration extension.The optional
typefield properly extendsWorkflowConfigwith theWorkflowTypediscriminator, maintaining backward compatibility while enabling processor workflow classification.
483-500: Tripwire variant is well-structured and properly integrated.The new
'tripwire'variant ofWorkflowResultis correctly designed:
- Mirrors the structure of other variants (
success,failed,suspended) consistently- Correctly requires the
tripwirefield (containingStepTripwireInfowith reason, retry flag, metadata, and processor ID)- Appropriately excludes
errorfield (tripwire rejection is distinct from error status) andresultfield (workflow didn't complete)- Steps mapping is identical across all variants for consistency
- Properly includes
TracingPropertiesvia intersection for observabilityNote: Telemetry decorator requirements apply to consuming components and classes, not to type definition files. The type definitions themselves have proper tracing support through
TracingProperties.
| : finalStatus === 'tripwire' && chunk.payload.tripwire | ||
| ? { tripwire: chunk.payload.tripwire } | ||
| : {}), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
fd -e ts -e tsx packages/core/src | head -20Repository: mastra-ai/mastra
Length of output: 433
🏁 Script executed:
# Find ChunkType definition
rg -nP --type=ts 'type\s+ChunkType|interface\s+ChunkType' --max-count=10Repository: mastra-ai/mastra
Length of output: 186
🏁 Script executed:
# Search for ChunkType and tripwire definitions more broadly
rg -nP --type=ts 'ChunkType.*=|payload.*tripwire' --max-count=20Repository: mastra-ai/mastra
Length of output: 22628
🏁 Script executed:
cat -n packages/core/src/stream/types.ts | sed -n '630,660p'Repository: mastra-ai/mastra
Length of output: 1659
🏁 Script executed:
# Find TypedChunkType definition
cat -n packages/core/src/stream/types.ts | sed -n '639,645p'Repository: mastra-ai/mastra
Length of output: 437
🏁 Script executed:
# Search for payload structure and tripwire in the types file
rg -n 'payload.*tripwire|type.*Payload|interface.*Payload' packages/core/src/stream/types.tsRepository: mastra-ai/mastra
Length of output: 6248
🏁 Script executed:
cat -n packages/core/src/stream/types.ts | sed -n '328,340p'Repository: mastra-ai/mastra
Length of output: 591
🏁 Script executed:
# Also check StepTripwireData which is referenced in BaseChunkType
rg -n 'type StepTripwireData|interface StepTripwireData' packages/core/src/stream/types.tsRepository: mastra-ai/mastra
Length of output: 99
🏁 Script executed:
cat -n client-sdks/react/src/lib/ai-sdk/utils/toUIMessage.ts | sed -n '30,60p'Repository: mastra-ai/mastra
Length of output: 1154
🏁 Script executed:
# Also check around line 150-173 mentioned in the original comment
cat -n client-sdks/react/src/lib/ai-sdk/utils/toUIMessage.ts | sed -n '140,180p'Repository: mastra-ai/mastra
Length of output: 1426
🏁 Script executed:
rg -n 'WorkflowFinishPayload|workflow-finish' packages/core/src/stream/types.ts | head -20Repository: mastra-ai/mastra
Length of output: 94
🏁 Script executed:
# Find the payload structure for workflow-finish
cat -n packages/core/src/stream/types.ts | sed -n '415,435p'Repository: mastra-ai/mastra
Length of output: 608
🏁 Script executed:
cat -n packages/core/src/stream/types.ts | sed -n '560,575p'Repository: mastra-ai/mastra
Length of output: 546
🏁 Script executed:
# Also search for any payload type that has tripwire field
rg -n 'tripwire:' packages/core/src/stream/types.tsRepository: mastra-ai/mastra
Length of output: 42
🏁 Script executed:
cat -n packages/core/src/stream/types.ts | sed -n '564,585p'Repository: mastra-ai/mastra
Length of output: 718
🏁 Script executed:
# Search for tripwire in workflow-finish context more broadly
rg -B5 -A5 'workflow-finish' packages/core/src/stream/types.tsRepository: mastra-ai/mastra
Length of output: 304
🏁 Script executed:
rg -n 'workflow-finish.*tripwire|tripwire.*workflow-finish' client-sdks/react/src/lib/ai-sdk/utils/Repository: mastra-ai/mastra
Length of output: 42
🏁 Script executed:
# Check the test file for toUIMessage
rg -B3 -A3 'workflow-finish' client-sdks/react/src/lib/ai-sdk/utils/toUIMessage.test.ts | head -40Repository: mastra-ai/mastra
Length of output: 1064
🏁 Script executed:
rg -n "workflowStatus.*tripwire|'tripwire'" client-sdks/react/src/lib/ai-sdk/utils/toUIMessage.test.tsRepository: mastra-ai/mastra
Length of output: 187
🏁 Script executed:
# Find WorkflowRunStatus type definition
rg -n 'type WorkflowRunStatus|enum WorkflowRunStatus' packages/core/src/Repository: mastra-ai/mastra
Length of output: 132
🏁 Script executed:
# Search for tripwire in workflow context
rg -n "workflowStatus.*=.*'tripwire'" packages/core/src/Repository: mastra-ai/mastra
Length of output: 42
🏁 Script executed:
cat -n packages/core/src/workflows/types.ts | sed -n '215,225p'Repository: mastra-ai/mastra
Length of output: 298
🏁 Script executed:
# Also check the tripwire test cases in toUIMessage.test.ts
rg -B5 -A10 "type: 'tripwire'" client-sdks/react/src/lib/ai-sdk/utils/toUIMessage.test.ts | head -50Repository: mastra-ai/mastra
Length of output: 1036
🏁 Script executed:
rg -B5 -A15 "workflow-finish.*tripwire|tripwire.*workflow-finish" client-sdks/react/src/lib/ai-sdk/utils/toUIMessage.test.tsRepository: mastra-ai/mastra
Length of output: 42
🏁 Script executed:
# Search more broadly for workflow-finish with tripwire in tests
rg -B10 "workflowStatus.*'tripwire'|'tripwire'.*workflow" client-sdks/react/src/lib/ai-sdk/utils/toUIMessage.test.tsRepository: mastra-ai/mastra
Length of output: 42
🏁 Script executed:
cat -n client-sdks/react/src/lib/ai-sdk/utils/toUIMessage.ts | sed -n '34,50p'Repository: mastra-ai/mastra
Length of output: 803
🏁 Script executed:
# Check if there are any recent changes to workflow-finish handling
git log --oneline -n 20 -- client-sdks/react/src/lib/ai-sdk/utils/toUIMessage.tsRepository: mastra-ai/mastra
Length of output: 73
🏁 Script executed:
# Check git diff to see what was changed in this PR
git diff HEAD~1 client-sdks/react/src/lib/ai-sdk/utils/toUIMessage.ts | head -100Repository: mastra-ai/mastra
Length of output: 246
🏁 Script executed:
# Look at the workflow-finish in the actual code to see what metadata contains
rg -B5 -A5 'workflowStatus.*tripwire' packages/core/src/Repository: mastra-ai/mastra
Length of output: 42
🏁 Script executed:
# Check if there's any tripwire data being added to workflow-finish metadata
rg -n 'workflow-finish' packages/core/src/workflows/ | grep -i tripwireRepository: mastra-ai/mastra
Length of output: 42
🏁 Script executed:
# Look at step-finish handling which might show the pattern
cat -n client-sdks/react/src/lib/ai-sdk/utils/toUIMessage.ts | sed -n '60,120p'Repository: mastra-ai/mastra
Length of output: 2088
🏁 Script executed:
# Get the full workflow-finish payload structure including all fields
cat -n packages/core/src/stream/types.ts | sed -n '564,580p'Repository: mastra-ai/mastra
Length of output: 583
🏁 Script executed:
# Check if tripwire was supposed to be added to WorkflowFinishPayload
rg -A20 "type: 'workflow-finish'" packages/core/src/stream/types.tsRepository: mastra-ai/mastra
Length of output: 541
🏁 Script executed:
# Verify what the actual current state of the file is
head -1 client-sdks/react/src/lib/ai-sdk/utils/toUIMessage.tsRepository: mastra-ai/mastra
Length of output: 124
🏁 Script executed:
# Verify the tripwire chunk type definition
rg -B2 -A2 "type: 'tripwire'" packages/core/src/stream/types.tsRepository: mastra-ai/mastra
Length of output: 301
Remove the non-existent chunk.payload.tripwire check from the workflow-finish handler.
The workflow-finish chunk payload does not include a tripwire field. The payload structure contains only workflowStatus, output, and metadata. When workflowStatus is 'tripwire', the tripwire data is delivered via a separate chunk with type: 'tripwire' (already correctly handled at lines 150-173), not within the workflow-finish payload.
Lines 45-46 should be simplified to:
: {}
The condition finalStatus === 'tripwire' && chunk.payload.tripwire will never evaluate to true since chunk.payload.tripwire does not exist in the WorkflowFinishPayload type definition.
🤖 Prompt for AI Agents
In client-sdks/react/src/lib/ai-sdk/utils/toUIMessage.ts around lines 45-47,
remove the non-existent check for chunk.payload.tripwire inside the
workflow-finish handler and replace that conditional branch with an empty
object; specifically simplify the ternary that currently uses "finalStatus ===
'tripwire' && chunk.payload.tripwire ? { tripwire: ... } : {}" (or similar) so
it always yields {} for the workflow-finish path, since tripwire data comes from
separate 'tripwire' chunks and WorkflowFinishPayload contains only
workflowStatus, output, and metadata.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🧹 Nitpick comments (2)
packages/core/src/workflows/workflow.ts (2)
468-523: KeepmessageListin sync when processors return a new MessageList.In the
result instanceof MessageListbranches acrossinput,inputStep,outputResult, andoutputStep, you return updatedmessages/systemMessagesbut leavepassThrough.messageListpointing at the original list. If a processor chooses to return a newMessageListinstance instead of mutating the passed-in one, downstream processors will see stalemessageListwhilemessages/systemMessagesreflect the new state.Consider updating
messageListin the return value:- if (result instanceof MessageList) { - return { - ...passThrough, - messages: result.get.all.db(), - systemMessages: result.getAllSystemMessages(), - }; - } + if (result instanceof MessageList) { + return { + ...passThrough, + messageList: result, + messages: result.get.all.db(), + systemMessages: result.getAllSystemMessages(), + }; + }(And mirror this in the other phases.) This keeps the canonical
messageListand the derived arrays consistent for chained processors.Also applies to: 539-593, 621-673, 687-743
468-523: Factor out shared “apply processor messages to MessageList” logic.The message reconciliation logic (remove existing IDs, re-add messages with role-based handling, update/replace system messages) is largely duplicated across:
phase: 'input'(array and{ messages, systemMessages }branches),phase: 'inputStep',phase: 'outputResult',phase: 'outputStep'(with only the source'input'vs'response'differing).Extracting small helpers like
applyMessagesToMessageList(messageList, oldMessages, newMessages, source)andapplyResultWithSystemMessages(...)would reduce repetition and make it harder for these branches to drift behavior-wise.Also applies to: 539-593, 611-673, 687-743
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
packages/core/src/workflows/workflow.ts(12 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Run
pnpm typecheckto validate TypeScript types across all packages
Files:
packages/core/src/workflows/workflow.ts
**/*.{ts,tsx,js,jsx,json,md}
📄 CodeRabbit inference engine (CLAUDE.md)
Run
pnpm prettier:formatto format code andpnpm formatto run linting with auto-fix across all packages
Files:
packages/core/src/workflows/workflow.ts
packages/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
packages/**/*.{ts,tsx}: All packages must use TypeScript with strict type checking enabled
Use telemetry decorators for observability across components
Files:
packages/core/src/workflows/workflow.ts
🧠 Learnings (2)
📚 Learning: 2025-11-24T16:42:04.244Z
Learnt from: CR
Repo: mastra-ai/mastra PR: 0
File: packages/codemod/AGENTS.md:0-0
Timestamp: 2025-11-24T16:42:04.244Z
Learning: Applies to packages/codemod/src/codemods/v1/**/*.ts : Use `context.hasChanges = true` flag and `context.messages.push()` to track and report what transformations were made in the codemod
Applied to files:
packages/core/src/workflows/workflow.ts
📚 Learning: 2025-10-29T09:37:59.778Z
Learnt from: TheIsrael1
Repo: mastra-ai/mastra PR: 9247
File: packages/core/src/loop/network/index.ts:357-393
Timestamp: 2025-10-29T09:37:59.778Z
Learning: In the agent network routing completion stream (packages/core/src/loop/network/index.ts), when `completionStream.error` is true, the first stream should not have written any text (currentTextIdx should be 0), so the fallback reset of currentText and currentTextIdx does not cause duplication.
Applied to files:
packages/core/src/workflows/workflow.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (12)
- GitHub Check: Socket Security: Pull Request Alerts
- GitHub Check: test
- GitHub Check: test
- GitHub Check: test
- GitHub Check: Lint
- GitHub Check: Validate examples packages.json
- GitHub Check: Prebuild
- GitHub Check: test
- GitHub Check: test (hono)
- GitHub Check: test
- GitHub Check: Analyze (javascript-typescript)
- GitHub Check: Analyze (actions)
🔇 Additional comments (5)
packages/core/src/workflows/workflow.ts (5)
4-5: New imports and type wiring are consistent with downstream usage.The added imports for
CoreMessage,MastraDBMessage,MessageList,TripWire,Processor,ProcessorStepSchema/ProcessorStepOutputSchema,ProcessorStepOutput,ChunkType, andWorkflowTypeare all used in the processor step implementation and workflow typing below; nothing looks unused or mis-typed here.As per coding guidelines, please ensure
pnpm typecheck,pnpm prettier:format, andpnpm formathave been run after these changes.Also applies to: 8-11, 19-21, 25-26, 53-53
161-172: Processor createStep overload matches ProcessorStep schemas and implementation.The processor overload correctly exposes a
Step<\processor:${TProcessorId}`, any, typeof ProcessorStepSchema, typeof ProcessorStepOutputSchema, any, any, DefaultEngineType>, and the union in the implementation signature includesProcessor, with dispatch viaisProcessor(params)`. This aligns the public type with the actual execute implementation further down.Also applies to: 182-188
274-276: Tripwire chunk handling in agent streaming looks correct.Capturing
tripwirechunks in both legacy and vNext streaming paths, short-circuiting the loop, and then throwing aTripWirewith{ reason, retry, metadata, processorId }cleanly aborts the step without attempting to resolvestreamPromiseor return a normal result. Propagating the raw chunk in the vNext writer before throwing is also consistent with exposing tripwire events to the client.Also applies to: 281-285, 299-305, 308-319
420-431: Processor base context and stream handling are well-shaped.The
abortimplementation throwingTripWirewithprocessor.id, the inclusion ofretryCountandrequestContextinbaseContext, and the optionalmessageListonprocessOutputStreamall match the processor API described in the PR objectives and keep responsibilities clean between workflow and processor.Also applies to: 597-607
789-807:isProcessortype guard is robust and avoids Agent/Tool collisions.The guard checks for an object with a string
id, excludesAgentandToolinstances, and requires at least one processor method. This should safely distinguish processors from other step-like constructs for thecreateStepimplementation branch.
| // Handle Processor - wrap it as a workflow step | ||
| if (isProcessor(params)) { | ||
| const processor = params; | ||
| return { | ||
| // @ts-ignore - processor overload has specific id type | ||
| id: `processor:${processor.id}`, | ||
| description: processor.name ?? `Processor ${processor.id}`, | ||
| // @ts-ignore - Use discriminated union for input (better UI experience) | ||
| inputSchema: ProcessorStepSchema, | ||
| // @ts-ignore - Use flexible schema for output (allows any phase combination) | ||
| outputSchema: ProcessorStepOutputSchema, | ||
| execute: async ({ | ||
| inputData, | ||
| requestContext, | ||
| }: { | ||
| inputData: z.infer<typeof ProcessorStepSchema>; | ||
| requestContext: RequestContext; | ||
| }): Promise<ProcessorStepOutput> => { | ||
| // Cast to output type for easier property access - the discriminated union | ||
| // ensures type safety at the schema level, but inside the execute function | ||
| // we need access to all possible properties | ||
| const input = inputData as ProcessorStepOutput; | ||
| const { | ||
| phase, | ||
| messages, | ||
| messageList, | ||
| stepNumber, | ||
| systemMessages, | ||
| part, | ||
| streamParts, | ||
| state, | ||
| finishReason, | ||
| toolCalls, | ||
| text, | ||
| retryCount, | ||
| } = input; | ||
|
|
||
| // Create a minimal abort function that throws TripWire | ||
| const abort = (reason?: string, options?: { retry?: boolean; metadata?: unknown }): never => { | ||
| throw new TripWire(reason || `Tripwire triggered by ${processor.id}`, options, processor.id); | ||
| }; | ||
|
|
||
| // Base context for all processor methods - includes requestContext for memory processors | ||
| const baseContext = { | ||
| abort, | ||
| retryCount: retryCount ?? 0, | ||
| requestContext, | ||
| }; | ||
|
|
||
| // Pass-through data that should flow to the next processor in a chain | ||
| // This enables processor workflows to use .then(), .parallel(), .branch(), etc. | ||
| const passThrough = { | ||
| phase, | ||
| // Auto-create MessageList from messages if not provided | ||
| // This enables running processor workflows from the UI where messageList can't be serialized | ||
| messageList: | ||
| messageList ?? | ||
| (Array.isArray(messages) | ||
| ? new MessageList() | ||
| .add(messages as MastraDBMessage[], 'input') | ||
| .addSystem((systemMessages ?? []) as CoreMessage[]) | ||
| : undefined), | ||
| stepNumber, | ||
| systemMessages, | ||
| streamParts, | ||
| state, | ||
| finishReason, | ||
| toolCalls, | ||
| text, | ||
| retryCount, | ||
| }; | ||
|
|
||
| // TripWire errors are NOT caught here - they bubble up to halt the workflow |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Guard against messages being undefined before mapping IDs in processor phases.
In several branches you do:
const existingIds = (messages as MastraDBMessage[]).map(m => m.id);
passThrough.messageList.removeByIds(existingIds);If a processor workflow is invoked with a messageList but without a messages array (which is allowed by the schema), messages will be undefined and this will throw at runtime in:
processInputarray/object branches (Line 486 and 503),processInputSteparray/object branches (Line 555 and 572),processOutputResultarray/object branches (Line 635 and 652),processOutputSteparray/object branches (Line 706 and 723).
You already enforce passThrough.messageList but not messages, so this is a real edge case.
Recommend guarding the map or deriving IDs from the MessageList instead. Minimal fix example for one site:
- const existingIds = (messages as MastraDBMessage[]).map(m => m.id);
+ const existingIds = Array.isArray(messages)
+ ? (messages as MastraDBMessage[]).map(m => m.id)
+ : [];Apply the same pattern in the corresponding branches for inputStep, outputResult, and outputStep.
Also applies to: 468-523, 539-593, 611-673, 687-743
🤖 Prompt for AI Agents
In packages/core/src/workflows/workflow.ts around lines 383-455 (and also in the
other processor-phase sections at 468-523, 539-593, 611-673, 687-743), the code
assumes messages is an array and calls (messages as MastraDBMessage[]).map(...),
which will throw if messages is undefined; guard against that by deriving
existingIds only when messages is a defined array (e.g., const existingIds =
Array.isArray(messages) ? messages.map(m => m.id) : passThrough.messageList ?
passThrough.messageList.getIds() /* or [] if MessageList has no getter */ :
[];), and only call passThrough.messageList.removeByIds(existingIds) with a
proper array (possibly empty); apply this same defensive check/derivation to
each of the four branches listed (processInput array/object, processInputStep
array/object, processOutputResult array/object, processOutputStep array/object)
so removal calls never receive undefined.
| /** Type of workflow - 'processor' for processor workflows, 'default' otherwise */ | ||
| public type: WorkflowType = 'default'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Preserve workflow.type when cloning workflows.
You added a type: WorkflowType field and wire it from WorkflowConfig into the constructor with this.type = type ?? 'default', but cloneWorkflow doesn’t pass workflow.type into the new Workflow config. Cloned processor workflows will therefore be downgraded to type: 'default'.
Recommend copying type in cloneWorkflow:
const wf: Workflow<DefaultEngineType, TSteps, TWorkflowId, TState, TInput, TOutput, TPrevSchema> = new Workflow({
id: opts.id,
inputSchema: workflow.inputSchema,
outputSchema: workflow.outputSchema,
steps: workflow.stepDefs,
mastra: workflow.mastra,
- options: workflow.options,
+ options: workflow.options,
+ type: workflow.type,
});This keeps cloned workflows’ type semantics aligned with the original.
Also applies to: 918-919, 933-933, 843-853
🤖 Prompt for AI Agents
In packages/core/src/workflows/workflow.ts around lines 843-853, 888-889,
918-919, and 933, cloneWorkflow currently omits copying workflow.type into the
new Workflow config so cloned processor workflows become 'default'; update
cloneWorkflow to pass the source workflow.type (e.g., type: workflow.type) into
the new WorkflowConfig for all clone paths so the constructed Workflow preserves
the original type.
- Add runtime validation for ProcessorStepOutput shape before casting - Add defensive 'part' in result check before extracting processed part PR comments: - #10947 (comment) - #10947 (comment) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
Add tripwire preservation to executeConditional for proper status conversion in fmtReturnValue, matching the pattern used in executeParallel. PR comment: #10947 (comment) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
| execute: async ({ | ||
| inputData, | ||
| requestContext, | ||
| }: { | ||
| inputData: z.infer<typeof ProcessorStepSchema>; | ||
| requestContext: RequestContext; | ||
| }): Promise<ProcessorStepOutput> => { | ||
| // Cast to output type for easier property access - the discriminated union | ||
| // ensures type safety at the schema level, but inside the execute function | ||
| // we need access to all possible properties |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if it'd be better if this whole execute function lived in the ../processors dir and just import here executeProcessorWorkflowStep or something. Just so the processor related code is separated from the rest of generic workflows stuff since a lot of this code used to live in the processor runner. Not a huge deal though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe even ProcessorRunner.executeWorkflow({ inputData, requestContext }) ?
| @@ -0,0 +1,425 @@ | |||
| # Processor Enhancements Implementation Plan | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh yeah we can remove this
| tripwire: true, | ||
| tripwireReason, | ||
| }), | ||
| tripwire, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
for agent legacy should we just leave it as the old tripwireReason? 🤷♂️
| payload: {}, | ||
| }, | ||
| }; | ||
| } else if (result.status === 'tripwire') { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did you try out tripwire in inngest? It might need some special handling since inngest has its own error retrying built in. If you throw any Error that is not a NonRetriableError, it will retry the step by default. Might need to throw a NonRetriableError with cause of tripwire data or something. Worth testing out though.
| memory: new Set(Array.from(this.memoryMessages.values()).map(m => m.id)), | ||
| output: new Set(Array.from(this.newResponseMessages.values()).map(m => m.id)), | ||
| input: new Set(Array.from(this.newUserMessages.values()).map(m => m.id)), | ||
| context: new Set(Array.from(this.userContextMessages.values()).map(m => m.id)), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you remember why this was needed?
Addresses #7923 - Better processor support in Mastra.
There were a lot of missing features of processors, all these new features are added to close the loop. The big ones are
processOutputStepadded which runs after every step.What's new:
1. Retry mechanism with LLM feedback - Processors can now request retries with feedback that gets sent back to the LLM:
Configure with
maxProcessorRetries(default: 3). Rejected steps are preserved inresult.steps[n].tripwire. Retries are only available inprocessOutputStepandprocessInputStep. It will replay the step with additional context added.2. Workflow orchestration for processors - Processors can now be composed using workflow primitives:
Every processor array that gets passed to an agent gets added as a workflow

3. Extended tripwire API -
abort()now accepts options for retry control and typed metadata:4. New
processOutputStepmethod - Per-step output processing with access to step number, finish reason, tool calls, and retry count.5. Workflow tripwire status - Workflows now have a
'tripwire'status distinct from'failed', properly bubbling up processor rejections.6. UI updates -
Summary by CodeRabbit
New Features
Bug Fixes / Behavior
Documentation
✏️ Tip: You can customize this high-level summary in your review settings.