You can now give your agents a durable, thread-scoped goal to work towards. Set an objective using setObjective, and an LLM judge scores each loop until the goal is met or a run budget is exhausted.
On each completed loop, the judge scores the conversation against the objective and returns 1 or 0 — a pass ends the loop, a fail injects feedback prompting the agent to try again. Evaluations are emitted as goal chunks containing the objective, status, and results array with a score, and reason. For example:
{
"objective": "Write a detailed report for all LLM releases in 2026",
"iteration": 1,
"maxRuns": 10,
"passed": true, // or false
"status": "done", // or "active" or "paused"
"pausedReason": null,
"judgeFailed": false,
"waitingForUser": false,
"results": [
{
"score": 1,
"passed": true,
"reason": "The goal to write a detailed report on 2026 LLM releases has been fully satisfied...",
"scorerId": "goal-scorer",
"scorerName": "Goal (LLM)",
"duration": 3054
}
]
// ...
}Before goals, keeping an agent focused on one objective across multiple messages meant re-supplying the success criteria on every .generate() or .stream() call, or using an isTaskComplete check. With goals, the objective is set once on the thread and the judge runs inside the loop where it decides to pass or fail on each iteration.
You can manage objectives using setObjective, getObjective, updateObjectiveOptions, and clearObjective — and if maxRuns is exhausted the objective stays active, letting you resume the loop again from where it left off.
Get started
Goals require a configured storage backend and a memory-backed thread.
@mastra/core@1.42.0 or later. Added in PR #17889.Add goal to your agent config, defining a model for the judge and value for maxRuns:
import { Agent } from "@mastra/core/agent";
import { Memory } from "@mastra/memory";
import { exaSearchTool } from "../tools/exa-search-tool";
export const researchAgent = new Agent({
id: "research-agent",
name: "Research Agent",
instructions: "You research topics by calling the search tool with different queries...",
model: "anthropic/claude-opus-4-8",
tools: { exaSearchTool },
memory: new Memory(),
goal: {
judge: "anthropic/claude-haiku-4-5", // smaller model to run evaluations
maxRuns: 10
}
});Set the objective on a thread using setObjective(), then pass the objective to .stream():
const agent = mastra.getAgent("researchAgent");
const threadId = "research-123";
const resourceId = "acme-research-agent";
const objective = "Write a detailed report for all LLM releases in 2026";
await agent.setObjective(objective, { threadId, resourceId });
const stream = await agent.stream(objective, {
memory: { thread: threadId, resource: resourceId }
});
console.log(chunk.type);
switch (chunk.type) {
case "tool-call":
console.log(chunk.payload);
break;
case "tool-result":
console.log(chunk.payload);
break;
case "goal":
console.log(chunk.payload);
break;
}
}
const record = await agent.getObjective({ threadId });
console.log(`objective=${record?.objective} status=${record?.status}`);For more information and full configuration options, see:
Appendix
Chunk names emitted by stream.fullStream during a goal-driven run:
Run lifecycle — one pair per .stream() call:
start: The agent run has startedfinish: The run has ended
Step lifecycle — one pair per iteration through the LLM (model call + tools + response):
step-start: A new step beginsstep-finish: The current step ends
Text streaming — one set per assistant text block:
text-start: The model begins emitting texttext-delta: Each chunk of streamed text (many per block)text-end: The text block is complete
Tool calls — the model streams the args before invoking:
tool-call-input-streaming-start: The model begins writing the tool's argstool-call-delta: A chunk of the args JSONtool-call-input-streaming-end: Args finished streamingtool-call: The parsed tool call (toolName,toolCallId,args)tool-result: The tool executed and returned (toolName,toolCallId,result)
Signals — state and notification signals projected into the model's context:
data-signal: A signal (including the current<current-objective>from the goal system)
Goals — one per evaluation from the judge:
goal:objective,iteration,maxRuns,passed,status,results,reason,pausedReason,waitingForUser,judgeFailed,duration,timedOut,maxRunsReached,suppressFeedback,pending,activity
