@mastra/core
Version:
118 lines (83 loc) • 6.27 kB
Markdown
> Discover all available pages from the documentation index: https://mastra.ai/llms.txt
# Goals
**Added in:** `/core@1.42.0`
> **Beta:** The Goals feature is in beta stage and subject to breaking changes in minor versions until it graduates from its beta status.
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's 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/capabilities/subagents): 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.
## 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/capabilities/subagents) instead.
## Quickstart
Goals require a configured [storage](https://mastra.ai/docs/storage/overview) 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 '/core/agent'
const worker = new Agent({
id: 'worker',
name: 'worker',
instructions: 'You complete software tasks end to end.',
model: 'openai/gpt-5.6-sol',
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 `<current-objective>` in its context without extra setup.
## 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 and the objective is marked `paused`. Raise `maxRuns`, then resume the objective to continue.
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 performs no scoring or budget consumption and emits 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({
id: 'worker',
name: 'worker',
instructions: 'You complete software tasks end to end.',
model: 'openai/gpt-5.6-sol',
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 isn't 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 })
```
Objective records include an optional `activeDurationMs` value for user interfaces that display active pursuit time. Mastra advances this value while an agent runs toward an active objective and checkpoints it when the run ends or waits for tool approval. Missing values represent zero, and the duration measures agent execution rather than the goal's wall-clock age.
Per-objective values written by `setObjective` / `updateObjectiveOptions` take precedence over the agent's `goal` config, and that precedence is remembered in thread state. 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/capabilities/subagents): `isTaskComplete` and the rubric scorer
- [Signal providers](https://mastra.ai/docs/long-running-agents/signal-providers): how the objective is projected into context
- [Memory storage](https://mastra.ai/docs/storage/overview): the storage backend goals require