When you build an agent for multi-step tasks, you need to track its progress, decide when to ask for approval, and retain enough context for it to continue.
That means handling state, permissions, and execution alongside the model calls.
Mastra Agent Controller and LangChain Deep Agents provide much of that infrastructure. If you work in TypeScript, Mastra gives you a native runtime. Deep Agents is available in both Python and JavaScript/TypeScript, with capabilities built on LangChain and LangGraph.
What is Mastra Agent Controller?
Mastra Agent Controller is a TypeScript runtime for managing an agent across an ongoing conversation. A controller hosts the agent, with three separate responsibilities underneath it:
- Session: handles turns, tool approvals, and live events for a user or task.
- Storage: persists the thread and its messages.
- Memory: selects the history passed to the model on later turns.
That separation gives you control over context. It also means storing a conversation does not automatically make the agent remember it.
Agent Controller includes user questions, plan approval, task tracking, and subagents. Modes change the agent's instructions and tools within the same conversation.
It ships inside @mastra/core and is still in beta, with possible breaking changes without a major version bump. The Agent Controller overview covers its APIs.
What is LangChain Deep Agents?
Deep Agents is an agent library built on LangChain and LangGraph. In Python, create_deep_agent includes:
- Filesystem tools for intermediate work and context offloading.
- Subagents for delegated tasks.
- Summarization for longer conversations.
Skills supply instructions from files, while structured todo planning is optional middleware.
You invoke the agent with messages and a thread ID. A LangGraph checkpointer saves its state between invocations, including approval interrupts, and LangGraph handles streaming.
The package is deepagents. A JavaScript implementation runs on LangGraph for JavaScript, so you can use either library in TypeScript.
Mastra Agent Controller vs. Deep Agents at a glance
| Capability | Mastra Agent Controller | LangChain Deep Agents |
|---|---|---|
| Language | TypeScript-native | Python and JavaScript |
| Application API | Create a session, subscribe to events, and send turns | Invoke an agent with messages and a thread ID |
| Tool approval | Allow, ask, or deny by tool or category; live session grants | Configure tool interrupts and resume the graph with a review decision |
| Persistence | Storage persists messages; memory selects model context | A checkpointer persists thread-scoped graph state |
| Subagents | Declared types with tools, model, and step limits; session lifecycle events | Isolated subagents, with general-purpose and specialized definitions |
| Context management | Observational memory can compress history in the background | Summarization and filesystem offloading during execution |
| Additional controls | Modes, user questions, plan approval, and task tracking | Filesystem tools, skills, and optional todo planning in Python |
Mastra Agent Controller vs. Deep Agents: A feature-by-feature comparison
Once you get past the basic agent loop, you’ll see the differences clearly. How do you pause for approval? What survives a restart? And what happens when the conversation gets too long?
Here’s how each handles those parts.
1. Sessions, execution, and agent modes
Mastra gives you a live session through which you manage the agent. Messages, approvals, mode changes, and progress events all belong to that session. Modes let you change instructions and available tools without starting another conversation.
Deep Agents exposes the agent as a LangGraph graph. You invoke it with a thread ID, stream its execution, and invoke it again to continue or resume interrupted work. The checkpointer maintains continuity between those calls.
| Application control | Mastra Agent Controller | Deep Agents |
|---|---|---|
| Main interface | A live session for turns, approvals, and events | Graph invocations associated with a thread ID |
| Changing behavior | Modes change instructions and tools within the conversation | Agent configuration and middleware define behavior |
| Planning and interaction | Built-in user questions, plan approval, and task tracking | Tool interrupts for review; optional todo middleware in Python |
Verdict: Mastra's built-in modes change the shared agent's instructions and tools without replacing the Session or thread. This lets users move from read-only planning to approved execution without starting over or wiring up a handoff between separate agents. Deep Agents manages behavior with continuity provided by LangGraph checkpoints.
2. Tool permissions and human approval
Mastra evaluates tool calls against permission policies. You can allow, ask, or deny by tool or category, and grant access for the remainder of a live session.
Deep Agents uses configured tool interrupts. When a call needs review, execution pauses and returns control to you. You resume the graph with an approval, edit, or rejection decision. The current Python API also supports conditional interrupts, and its filesystem tools have path-based permission rules.
| Permission behavior | Mastra Agent Controller | Deep Agents |
|---|---|---|
| Policy scope | Tool and category rules, plus live session grants | Tool review, conditional interrupts, and filesystem permission rules |
| Approval handling | Respond through the active session | Resume the checkpointed graph with a decision |
Mastra's live approval gate and session grants do not automatically survive a process restart, and resumable tool suspensions are separate. The Mastra approval docs explain that distinction, while the Deep Agents approval docs cover checkpointed and conditional interrupts.
Verdict: Choose Mastra when you want tool-category policies and approval decisions tied to a live session. Choose Deep Agents when you want review to pause a checkpointed graph that you resume in a later invocation. Both support human review; compare the policy scope and restart behavior your application needs.
3. Persistence and conversation memory
Mastra separates what you store from what you send back to the model. Storage keeps thread messages, while memory selects the history included in later requests. Our blog post on AI Agent Memory explains how stored history, working memory, semantic recall, and observational memory contribute to that context.
Deep Agents uses a LangGraph checkpointer to persist thread-scoped graph state. You continue with the same thread ID, restoring the messages and execution state needed for the next invocation.
| Persistence concern | Mastra Agent Controller | Deep Agents |
|---|---|---|
| Conversation history | Stored thread messages | Messages in checkpointed graph state |
| Model context | Selected by the memory configuration | Managed within the agent's message and context-processing flow |
| Continuing after a restart | Bind a new session to the stored thread | Invoke with the same thread ID and persistent checkpointer |
| Live execution state | Session state is distinct from stored history | Checkpoints retain the state used to resume graph execution |
With Mastra, configure both storage and memory: your UI can display stored messages even when those messages are absent from the model's context. With Deep Agents, choose a persistent checkpointer if you need state to survive a restart; an in-memory one loses its contents when the process exits.
For either product, distinguish conversation history from execution recovery. Reopening a conversation and resuming a pending tool operation place different demands on your runtime.
Verdict: Choose Mastra if you want to configure stored history and model-visible memory separately. Choose Deep Agents if saving and resuming graph execution is central to your application. Either way, configure durable storage before relying on continuity across restarts.
4. Subagent configuration and context
Both products can delegate work to subagents. The differences are in how you define their responsibilities, what context they receive, and how you observe them. Mastra's subagent documentation covers delegation controls, request context, and lifecycle hooks.
| Delegation control | Mastra Agent Controller | Deep Agents |
|---|---|---|
| Definition | Declare types with instructions, tools, and optional model and step budget | Use a general-purpose spawner or declare specialized subagents |
| Context handling | Delegation hooks and request-context controls | Isolated subagent context with a delegated task |
| Visibility | Dedicated session events for start and completion | Spawner tool calls, with additional detail through LangGraph streaming |
Mastra gives you hooks to adjust delegation and inspect the result. Its request context carries application data across that boundary; it is separate from the model's conversation history.
Deep Agents' isolated context keeps child tasks separate from the parent's accumulated history, so each delegation needs enough information to complete the task. You can use LangGraph streaming when the spawner call alone is not enough for your UI.
Verdict: Choose Mastra if you need delegation hooks and subagent lifecycle events integrated with your session. Choose Deep Agents if you want to start with a general-purpose spawner and add specialized subagents as needed. With either, define what each child task needs to know and what it should return.
5. Context compaction and access to history
Both reduce the context sent to the model as a conversation grows. They differ in when compression happens and how older information remains available.
| Context behavior | Mastra observational memory | Deep Agents summarization |
|---|---|---|
| Compression | An observer creates dated observations; a reflector compresses those observations later | The agent summarizes older messages |
| Timing | Background observation with buffering enabled | Summarization during execution when the threshold is reached |
| Older information | Observations enter model context; original messages remain in storage | Full history is written to a file the agent can read again |
| Visibility | Structured memory and buffering events | Summary and history file available during execution |
Mastra's background buffering prepares observations before the context threshold is reached. Its async-buffering release post explains how buffered results activate and which streaming events report the work. Deep Agents performs summarization during execution and retains a readable history file.
For your workload, I would check whether background compression keeps up with incoming turns and whether the agent retrieves omitted details when it needs them.
Mastra also retains the original messages. Its observational memory documentation describes an optional recall tool that lets the agent retrieve source messages behind observations. The observational memory announcement explains the observer/reflector design.
Verdict: Choose Mastra if you want background observation and reflection to manage context in your TypeScript application. Choose Deep Agents if file-based context offloading and summarization fit how your agent works. Both can preserve source history; verify how your agent retrieves details after compression.
6. Observability and UI integration
Mastra exposes a session event stream that brings together the agent's messages, tools, approvals, and delegated work. Deep Agents exposes execution through LangGraph streaming, message metadata, and interrupt payloads.
| Observability surface | Mastra Agent Controller | Deep Agents |
|---|---|---|
| Live data | Run lifecycle, messages, tool input, approvals, suspensions, subagents, token usage, and compaction | Messages, LangGraph node and streaming events, and interrupts |
| Application integration | Subscribe to the session | Consume the relevant graph streams and results |
| Tracing | Mastra traces, exporters, and OpenTelemetry integration | LangSmith hosted tracing |
With Mastra, you can build a progress view around the session subscription. With Deep Agents, you consume graph streams and interrupt payloads to show execution and approval state. Your existing UI and tracing integrations will affect how much code either approach requires.
Beyond session events, Mastra's tracing documentation covers spans, exporters, and OpenTelemetry integration. If you already use LangSmith, Deep Agents can use that hosted tracing alongside LangGraph streaming.
Verdict: Choose Mastra if you want to build your progress and approval UI around one session event stream. Choose Deep Agents if you already use LangGraph streaming and LangSmith. I would weigh the integrations you already have here: they can matter more than the number of events either library exposes.
Mastra Agent Controller or LangChain Deep Agents, which one should you use?
- If you're building in TypeScript, start with Mastra Agent Controller when your agent shares an ongoing conversation with a user. Sessions, permission rules, and events reduce the application code you need around the agent. The costs are more configuration and a beta API.
- If you're building in Python, start with Deep Agents. LangChain and LangGraph provide the tools, state, and pause/resume behavior together. You still need to configure approvals and durable storage for the operations that require them.
If you're already using LangGraph in TypeScript, Deep Agents' JavaScript package is another option.
But I would compare Mastra's session and permission APIs with your existing setup, then validate approval and restart behavior against your application's requirements.
Get started with Mastra Agent Controller
If you're ready to try Mastra for your agent workflows, here's a quick starter guide. You'll create an agent, open a session, and stream your first response using Agent Controller.
Install the packages
In a TypeScript project using ES modules ("type": "module" in package.json), install the latest releases:
npm install @mastra/core@latest @mastra/libsql@latest zod@latest
npm install --save-dev tsx@latest typescript@latest @types/node@latestAdd your model provider's key to .env:
OPENAI_API_KEY=your-api-key
Create the agent and controller
Create agent-controller.ts and import the agent, controller, and storage classes:
import { Agent } from '@mastra/core/agent';
import { AgentController } from '@mastra/core/agent-controller';
import { LibSQLStore } from '@mastra/libsql';
const controller = new AgentController({
id: 'release-planning',
agent: new Agent({
id: 'release-assistant',
name: 'Release assistant',
instructions:
'Help developers prepare releases. Give concise, actionable checklists.',
model: 'openai/gpt-5.6-sol',
}),
storage: new LibSQLStore({
id: 'release-history',
url: 'file:./release-history.db',
}),
});
await controller.init();The model ID matches the current quickstart. Storage writes conversation history to a local database; to include that history in subsequent model calls, add a memory configuration.
Open a session and stream a response
Append this to the same file:
const releaseSession = await controller.createSession({
resourceId: 'developer-1',
});
const stopListening = releaseSession.subscribe(update => {
if (update.type !== 'message_update') return;
if (update.event.type === 'text-delta') {
process.stdout.write(update.event.delta);
}
});
try {
await releaseSession.sendMessage({
content: 'Give me a five-step checklist for releasing a TypeScript library.',
});
process.stdout.write('\n');
} finally {
stopListening();
}Run it with your .env loaded:
npx tsx --env-file=.env agent-controller.tsThe response streams to your terminal. In an application, the same subscription can feed your UI. From here, add tool approval policies or agent modes when you need more control over what the agent does.
Ready to take it further? Sign up to try Mastra.
