Claude Managed Agents vs Trigger.dev: when to use which, and how they combine
Claude Managed Agents could be thought of as a Claude Code in the cloud. They have their own computer. And you can attach tools.
Trigger.dev is durable infrastructure for agents you write yourself, in TypeScript, with any model. You get the same shape of thing, a long-running agent with a computer and tools, but you own the loop.
This is a "when to use which, and how they combine" comparison, not a "which is better". Everything below was checked against the official docs on October 14, 2026.
TL;DR
- Claude Managed Agents gives you Claude Code over API - it's out of the box with super fast setup.
- Trigger.dev gives you full customization and your tasks can be long running with full visibility.
- Trigger.dev is more suitable if you're building a product on top of AI agents because you have more control and visibility.
- You can also use Trigger in conjunction with Claude Managed Agents, where you have retries and lots of observability over what Claude Managed Agents are doing.
Claude Managed Agents
Anthropic describes it as a "pre-built, configurable agent harness that runs in managed infrastructure". It's in beta: every request needs the managed-agents-2026-04-01 beta header, and the SDK sets it for you.
There are four concepts: an agent (model, system prompt, tools, MCP servers, skills), an environment (an Anthropic-managed cloud sandbox, or a self-hosted sandbox on your own infrastructure), a session (one running agent instance in an environment) and events (the messages between your app and the agent).
They have their own computer. Each session gets a fresh Linux container with bash, file read/write/edit, glob, grep, web search and web fetch, plus pre-installed runtimes. You can pre-install packages with apt, cargo, gem, go, npm and pip, and lock networking down to an allowlist of hosts.
You can attach an arbitrary number of tools. On top of the built-in toolset there are three ways to add more: remote MCP servers, custom tools (Claude emits a structured request, your code runs it and returns the result), and skills. The docs don't publish a cap on how many.
Anthropic doesn't publish a maximum duration for a session. The overview says it's best for "tasks that run for minutes or hours with multiple tool calls", and the docs describe sessions as "long-running" and able to "resume cleanly after pauses", but there's no published maximum. What you do get is a session budget: a hard dollar cap, and when the session hits it, it pauses and goes idle rather than terminating. Transient errors are retried automatically (the session goes into a rescheduling status).
You can access them directly from the terminal like this:
ant beta:sessions connect "$SESSION_ID"
Or you can create, run, destroy them etc. with code or via API. The snippet below opens a stream, sends a user message as an event, and reads events back until the session goes idle:
import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic();
const session = await client.beta.sessions.create({
agent: process.env.AGENT_ID!,
environment_id: process.env.ENVIRONMENT_ID!,
});
const stream = await client.beta.sessions.events.stream(session.id);
await client.beta.sessions.events.send(session.id, {
events: [
{
type: "user.message",
content: [{ type: "text", text: "Write a script that prints the first 20 Fibonacci numbers, run it, and verify the output." }],
},
],
});
for await (const event of stream) {
if (event.type === "agent.message") {
for (const block of event.content) {
if (block.type === "text") process.stdout.write(block.text);
}
} else if (event.type === "session.status_idle") {
break;
}
}
Observability is the Console session viewer (Session event stream): a transcript grouped by model request, every tool call with inputs and results, token counts and cost per session. The full event history is also available from the API, and you can subscribe to webhooks.
Pricing is two lines: tokens at the normal model rates, plus session runtime at $0.08 per session-hour, metered on time in the running status.
Best fit: you want a Claude agent with a computer today, and you don't want to build or run the harness.
Limits: it only runs Claude. The loop, the context management and the sandbox are Anthropic's, so you configure them rather than change them. It's beta, with no published maximum session length. And because sessions store conversation history and sandbox state server-side, Managed Agents isn't currently eligible for Zero Data Retention or HIPAA BAA coverage.
Trigger.dev
With Trigger.dev, you can achieve the same thing as Claude Managed Agents but you don't use Claude Code as the harness - you write your own agent loop (e.g. using AI SDK).
A task is a TypeScript function with a retry policy, deployed from the CLI, that Trigger.dev runs on a long-running machine. Here's an agent loop inside one, using the Vercel AI SDK:
import { task } from "@trigger.dev/sdk";
import { generateText, tool, stepCountIs } from "ai";
import { anthropic } from "@ai-sdk/anthropic";
import { z } from "zod";
// Like Managed Agents' allowed_hosts: don't let the model fetch arbitrary URLs.
const ALLOWED_HOSTS = ["docs.example.com", "api.example.com"];
export const researchAgent = task({
id: "research-agent",
maxDuration: 3600, // compute seconds; time in Trigger.dev waits doesn't count
retry: { maxAttempts: 3 },
run: async (payload: { prompt: string }) => {
const result = await generateText({
model: anthropic("claude-opus-5-5"),
prompt: payload.prompt,
tools: {
fetchPage: tool({
description: "Fetch a web page as text",
inputSchema: z.object({ url: z.string().url() }),
execute: async ({ url }) => {
if (!ALLOWED_HOSTS.includes(new URL(url).hostname)) {
return `Blocked: ${url} is not on the allowlist`;
}
return (await fetch(url)).text();
},
}),
},
stopWhen: stepCountIs(25),
experimental_telemetry: { isEnabled: true }, // model and tool calls become spans in the run's trace
});
return { text: result.text, steps: result.steps.length };
},
});
Swap anthropic(...) for any other AI SDK provider and the rest is unchanged.
And you can bring your own sandbox so that you can emulate the experience of Claude Code. The docs show two routes. One is a sandbox provider: the code sandbox pattern warms an E2B sandbox at the start of each turn (Vercel Sandbox, Modal and Daytona are listed as alternatives) and exposes it to the model as an executeCode tool. The other is to run the Claude Agent SDK inside a task, so Claude reads, edits and writes files in a workspace on the task's machine:
import { query } from "@anthropic-ai/claude-agent-sdk";
import { schemaTask, logger } from "@trigger.dev/sdk";
import { mkdtemp, rm, readdir } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { z } from "zod";
export const codeGenerator = schemaTask({
id: "code-generator",
schema: z.object({ prompt: z.string() }),
run: async ({ prompt }, { signal }) => {
const abortController = new AbortController();
signal.addEventListener("abort", () => abortController.abort());
const workDir = await mkdtemp(join(tmpdir(), "claude-agent-"));
try {
const result = query({
prompt,
options: {
abortController,
cwd: workDir,
maxTurns: 10,
permissionMode: "acceptEdits",
allowedTools: ["Read", "Edit", "Write", "Glob"],
},
});
for await (const message of result) {
logger.info("Agent message", { type: message.type });
}
return { filesCreated: await readdir(workDir, { recursive: true }) };
} finally {
await rm(workDir, { recursive: true, force: true });
}
},
});
Like Claude Managed Agents you can connect an arbitrary amount of tools. They're AI SDK tools, MCP servers, or plain functions, and the model can be anything the AI SDK supports.
Your tasks can be long running. Runs have no maximum duration, and Trigger.dev has seen users with runs lasting more than six months. You set a maxDuration per task (minimum 5 seconds, or timeout.None to let a run execute indefinitely), and it counts compute time only: a wait.for, a wait.forToken for a human approval, or a triggerAndWait on a child task doesn't count. Waits longer than 5 seconds aren't billed as compute, and on Trigger.dev Cloud a wait longer than 60 seconds checkpoints the process and releases the machine, so you aren't paying for a box to sit idle (self-hosted deployments block through waits instead). When an attempt throws, a new attempt runs run() again from the top according to the retry settings, and an out-of-memory crash can be retried on a bigger machine.
You also get full visibility over every run.
Every run has a trace view with a span per step and per tool call, logs, and a replay button. AI SDK calls can be captured as spans. Logs are kept for 1 day on Free, 7 days on Hobby and 30 days on Pro, and you can export traces with OpenTelemetry.
For chat-shaped agents there's chat.agent(), which runs each conversation as one long-lived task: durable sessions, mid-stream resume, recovery from crash or OOM, sub-agents and human-in-the-loop tool approvals, with the loop still written in the AI SDK.
Pricing is public: Free with $5 of monthly credit, Hobby at $10, Pro at $50, and compute billed per second only while a task is executing. Trigger.dev is Apache 2.0 and self-hostable.
Best fit: TypeScript teams that want to own the agent loop, pick the model, and need runs to survive crashes, redeploys and multi-day waits.
Limits: you write the loop. There's no prompt, tool registry or harness in the box, so the sandbox and the context management are yours to wire up (or bring from the AI SDK, Mastra, or the Claude Agent SDK). There's no Python SDK for defining tasks. And retries restart run() from the top, so side effects need idempotency keys.
Side by side
| Claude Managed Agents | Trigger.dev | |
|---|---|---|
| Agent loop | Anthropic's harness, configured not written | Yours (AI SDK, Mastra, Claude Agent SDK, anything) |
| Model | Claude only | Any |
| Computer | Fresh Linux container per session, cloud or self-hosted | The task's machine, plus any sandbox provider (E2B, Vercel Sandbox, Modal, Daytona) |
| Tools | Built-in toolset, MCP servers, custom tools, skills | AI SDK tools, MCP, plain functions |
| Max duration | Not published by Anthropic; docs say "minutes or hours" | None; users have had runs lasting more than six months. maxDuration is yours to set, and counts compute time only |
| Retries | Transient errors rescheduled automatically | Configurable per task, OOM retry on a bigger machine |
| Cost control | Session budget (hard dollar cap, pauses the session) | maxDuration, machine size, concurrency limits |
| Human in the loop | Permission policies and tool confirmations | wait.forToken, tool approvals in chat.agent() |
| Visibility | Console session viewer, events API, webhooks | Run trace view, logs, Realtime, OpenTelemetry export |
| Pricing | Tokens at list rates plus $0.08 per session-hour | Per-second compute while executing, $5 free credit a month |
| Status | Beta | GA, Apache 2.0, self-hostable |
Using them together
You can also use Trigger in conjunction with Claude Managed Agents, where you have retries and lots of observability over what Claude Managed Agents are doing.
The session is the unit Anthropic manages. The job around it, triggering it from a webhook or a schedule, queueing it, retrying when session creation or the stream fails, fanning out across many sessions, and recording what came back next to the rest of your pipeline, is a Trigger.dev task:
import { task, logger, metadata, AbortTaskRunError } from "@trigger.dev/sdk";
import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic();
export const managedAgentJob = task({
id: "managed-agent-job",
retry: { maxAttempts: 3 },
run: async (payload: { prompt: string }) => {
// Run metadata survives retries, so a retry reattaches to the session it
// already started instead of creating (and paying for) a second one.
let sessionId = metadata.get("sessionId") as string | undefined;
if (!sessionId) {
const session = await client.beta.sessions.create({
agent: process.env.AGENT_ID!,
environment_id: process.env.ENVIRONMENT_ID!,
budget: { type: "limit", max_list_cost: { amount: "500", currency: "USD" } }, // $5.00
initial_events: [
{ type: "user.message", content: [{ type: "text", text: payload.prompt }] },
],
});
sessionId = session.id;
metadata.set("sessionId", sessionId);
}
// 1. Wait for the turn to finish. The stream has no replay, so it's only
// used to wait; the answer is read from the persisted history afterwards.
const session = await client.beta.sessions.retrieve(sessionId);
if (session.status !== "idle") {
for await (const event of await client.beta.sessions.events.stream(sessionId)) {
if (event.type === "agent.tool_use" || event.type === "agent.mcp_tool_use") {
logger.info("Tool call", { type: event.type });
} else if (event.type === "session.error") {
throw new Error(`Session error: ${JSON.stringify(event.error)}`); // retried by the task
} else if (event.type === "session.status_idle" || event.type === "session.status_terminated") {
break;
}
}
}
// 2. Read the result from the event history.
let output = "";
let stopReason: string | undefined;
for await (const event of client.beta.sessions.events.list(sessionId)) {
if (event.type === "agent.message") {
for (const block of event.content) {
if (block.type === "text") output += block.text;
}
} else if (event.type === "session.status_idle") {
stopReason = event.stop_reason;
} else if (event.type === "session.status_terminated") {
throw new Error(`Session ${sessionId} terminated`);
}
}
// 3. Only end_turn means the agent finished. A session paused on a tool
// approval (requires_action) or at its budget (budget_reached) won't
// finish by retrying, so fail the run without one.
if (stopReason !== "end_turn") {
throw new AbortTaskRunError(`Session ${sessionId} stopped: ${stopReason ?? "stream ended early"}`);
}
return { sessionId, output };
},
});
Each tool call lands in the Trigger.dev run as a log line, and the session ID is in the run output so you can jump to the Console transcript. A session error or a dropped stream is retried by the task, and the retry reattaches to the same session rather than starting a second one. A session that stops for a tool approval or at its budget fails the run without a retry, since retrying wouldn't finish it.
FAQs
-
Is Claude Managed Agents the same as Claude Code? No. Anthropic calls it a pre-built agent harness with its own sandbox. It has the same shape as Claude Code (a loop, bash and file tools, a computer) but it's a separate product, and Anthropic's branding guidelines ask integrators not to present it as Claude Code.
-
Can I use a non-Claude model with Managed Agents? No. The agent's
modelis a Claude model. With Trigger.dev the model is whatever your loop calls. -
Which one is long running? Both. Anthropic doesn't publish a maximum session duration; its docs describe sessions as running for minutes or hours. Trigger.dev can run for days or weeks: there is no limit on the
maxDurationyou can set, Trigger.dev has seen users with runs lasting more than six months, and it counts compute time only, so a task can wait on a human for days without that counting. -
Do I need both? Not for a single session you start by hand. You need something like Trigger.dev when sessions are kicked off by your system, need to run in bulk, need to be retried, or need to sit inside a pipeline with other work.