# Goals **Added in:** `@mastra/core@1.42.0` A goal is a durable, thread-scoped objective: a standing instruction the agent keeps working toward across loop iterations until a judge model decides it is satisfied or a run budget is exhausted. The objective is persisted in thread state, so it survives reloads and is evaluated in-loop — even when a new message arrives in the middle of an already-running turn. Goals build on the same machinery as [`isTaskComplete`](https://mastra.ai/docs/agents/supervisor-agents): an LLM-as-judge scores the agent's output each iteration and gates the loop. The difference is that a goal is **durable** (stored in thread state, not passed per call) and is set and updated through `Agent` methods rather than per-`stream()` options. > **Note:** Goals are experimental and may change in a future release. ## When to use goals Use a goal when you want an agent to keep working toward a single objective across many iterations and messages, without re-supplying the success criteria on every call: - A standing objective the agent should pursue until a judge says it's done. - Work that should continue across mid-run messages (a message delivered into a live run is still judged against the goal). - An objective that must persist across thread reloads or process restarts. For a one-off completion check within a single `stream()` call, use [`isTaskComplete`](https://mastra.ai/docs/agents/supervisor-agents) instead. ## Quickstart Goals require a configured [storage](https://mastra.ai/docs/memory/storage) backend and a memory-backed thread. Add a `goal` config to the agent — a judge model is required for the goal to do anything — then set an objective for a thread: ```typescript import { Agent } from '@mastra/core/agent' const worker = new Agent({ name: 'worker', instructions: 'You complete software tasks end to end.', model: 'openai/gpt-5.5', memory, goal: { judge: 'openai/gpt-5-mini', maxRuns: 50, }, }) // Set the durable objective for a thread. await worker.setObjective('Add and test a /health endpoint', { threadId, resourceId, }) // The objective is judged each iteration until it's complete or maxRuns is hit. const stream = await worker.stream('Start working on the goal', { memory: { thread: threadId, resource: resourceId }, }) ``` The `goal` config auto-registers the state-signal projection, so the model always sees the current objective as `` in its context — no extra setup needed. ## How the goal step works A goal step runs inside the agentic execution loop, right after `isTaskComplete`. On a real candidate answer it scores the conversation against the objective and gates the loop: - **Not satisfied, budget remaining** → the loop continues; per-evaluation feedback is injected so the agent iterates. - **Satisfied** → the loop stops and the objective is marked `done`. - **Budget exhausted** (`runsUsed >= maxRuns`) → the loop stops but the objective stays `active`, so raising `maxRuns` later can resume it. The step is a no-op for background-task, mid-tool-loop, and working-memory-only iterations — the same gating as `isTaskComplete`. **The judge model is the activation switch.** If no judge resolves (neither the per-objective override nor the agent's `goal.judge`), the goal step does nothing: no scoring, no budget consumed, no `goal` chunk. Effective settings resolve as per-objective record value → agent `goal` config → built-in default (`maxRuns` `50`, a default judge prompt). By default the step uses a built-in LLM-as-judge scorer that returns `1` when the objective is achieved and `0` otherwise. Supply your own scorer with `goal.scorer` to customize judging. ```typescript const worker = new Agent({ name: 'worker', instructions: 'You complete software tasks end to end.', model: 'openai/gpt-5.5', memory, goal: { // A resolver function lets you inject provider credentials and read the // current judge selection at runtime; returning `undefined` keeps the // goal step a no-op. judge: ({ requestContext }) => resolveJudgeModel(requestContext), maxRuns: 30, prompt: 'Only mark the goal complete when tests pass.', }, }) ``` Each evaluation emits a typed `goal` stream chunk (`GoalEvaluationPayload`: `objective`, `iteration`, `maxRuns`, `passed`, `status`, `results`, `reason`, `duration`, `timedOut`, `maxRunsReached`, `suppressFeedback`) so a UI can show goal progress mid-run. ## Managing the objective Control the objective for a thread with `Agent` methods. All of them no-op when the run is not memory-backed (they require storage and a `threadId`): ```typescript // Read the current objective record. const record = await worker.getObjective({ threadId }) // Update options on the active objective (only provided fields are written; // unset fields fall back to the agent's `goal` config). await worker.updateObjectiveOptions({ threadId, maxRuns: 100 }) // Drop the objective. await worker.clearObjective({ threadId }) ``` Per-objective values written by `setObjective` / `updateObjectiveOptions` take precedence over the agent's `goal` config, and that precedence is remembered in thread state. > **Note:** See the [`GoalEvaluationPayload` in the ChunkType reference](https://mastra.ai/reference/streaming/ChunkType) for the full goal chunk shape. ## Related - [Supervisor agents](https://mastra.ai/docs/agents/supervisor-agents) — `isTaskComplete` and the rubric scorer - [Signal providers](https://mastra.ai/docs/agents/signal-providers) — how the objective is projected into context - [Memory storage](https://mastra.ai/docs/memory/storage) — the storage backend goals require