Claude Managed Agents (beta) operations
This client mirrors and verifies upstream-compatible behavior. Upstream runtimes remain the canonical protocol owners.
ClaudeManagedAgentClient is a stateful RuntimeClient over the Anthropic Managed Agents surface — a separate, beta API from /v1/messages covering the full agent lifecycle: persisted Agent configs, reusable Environments, stateful Sessions, session resources, multiagent threads, rubric-graded outcomes, memory stores, credential vaults, and scheduled deployments.
Every request carries the beta header `anthropic-beta: managed-agents-2026-04-01`. The client sets it (and anthropic-version) on construction, so no operation below needs to pass it. Auth is x-api-key (from apiKey) or Authorization: Bearer (from authToken, which takes precedence); one of the two is required. Base URL defaults to the Anthropic API (baseUrl to override).
Import from the same providers/claude entry as the stateless Messages client:
import { ClaudeManagedAgentClient } from "@cavi-ai/api-client/providers/claude";
const client = new ClaudeManagedAgentClient({
apiKey: process.env.ANTHROPIC_API_KEY,
agentId: "agt_…", // default agent for RuntimeClient runs
environmentId: "env_…", // default environment for RuntimeClient runs
});Provider kind is claude-managed-agents (aliases claude-agents, claude-teams); build a registry module with createClaudeManagedAgentProviderModule(config).
Mandatory flow. An Agent and an Environment must exist *before* any run — model and system prompt live on the persisted agent object, never on a session. Every run is a Session that references an agent + environment by id.
The universal run/stream semantics this client implements (startRun/getRun/cancelRun/streamRun) are documented once under runtime operations; the RuntimeClient section below only notes how they map onto Managed Agents sessions.
Runtime capabilities
Returns providerKind: "claude-managed-agents", protocolVersion: "managed-agents-2026-04-01", auth: { type: "api-key", required: true }, and supports: { runs: true, streaming: true }. Purely local — no HTTP call.
Agents
Persisted, versioned agent configs. Model, system prompt, tools, MCP servers, and skills live here; each POST to an existing agent mints a new immutable version.
archiveAgent is terminal (no unarchive): existing sessions keep running, new sessions can no longer reference it.
Request body / Parameters
CreateManagedAgentParams (also used by updateAgent):
| Field | Type | Required | Description |
|---|---|---|---|
name | string | required | Display name. |
model | string | required | Model id (e.g. claude-opus-4-8). |
system | string | optional | System prompt. |
description | string | optional | Free-text description. |
tools | Record<string, unknown>[] | optional | Tool definitions. |
mcpServers | Record<string, unknown>[] | optional | MCP server configs (mcp_servers). |
skills | Record<string, unknown>[] | optional | Skill definitions. |
multiagent | Record<string, unknown> | optional | Coordinator/roster config (see team provisioning). |
metadata | Record<string, unknown> | optional | Arbitrary metadata. |
Response
ManagedAgentAgent — { id, version?, name?, … }. Persist id (and version if you pin sessions to a specific one).
Example
const agent = await client.createAgent({
name: "researcher",
model: "claude-opus-4-8",
system: "You are a meticulous research assistant.",
tools: [{ type: "web_search_20250305", name: "web_search" }],
});
// agent.id -> "agt_…", agent.version -> 1Environments
Reusable container templates sessions are provisioned into. Updates apply to new containers only; existing sessions keep their config.
Request body / Parameters
CreateManagedAgentEnvironmentParams:
| Field | Type | Required | Description |
|---|---|---|---|
name | string | required | Display name. |
config | Record<string, unknown> | optional | Container config; defaults to cloud/unrestricted. Pass { type: "self_hosted" } for a self-hosted work queue. |
description | string | optional | Free-text description. |
metadata | Record<string, unknown> | optional | Arbitrary metadata. |
UpdateManagedAgentEnvironmentParams is the same shape with all fields optional.
Self-hosted work queue
For self_hosted environments the client exposes monitoring/control only — the tool-executing worker loop is a host-side concern with its own sandbox boundary.
Sessions
Stateful runs referencing a pre-created agent + environment. The stateful reads, resumability, and streaming the Messages API cannot provide.
updateSession is a session-local override (session must be idle); agent.tools, agent.mcp_servers, and vault_ids are full replacements, not merges. deleteSession is permanent (removes event history, container, checkpoints); archiveSession makes it read-only.
Request body / Parameters
CreateManagedAgentSessionParams:
| Field | Type | Required | Description |
|---|---|---|---|
agentId | string | required | Persisted agent to reference. |
environmentId | string | required | Environment to provision into. |
agentVersion | number | optional | Pin an agent version; omit for latest at create time. |
agentOverrides | Record<string, unknown> | optional | Session-local wire-shaped overrides (model/system/tools/…); null clears a field. Never merges. |
title | string | optional | Session title. |
resources | Record<string, unknown>[] | optional | Initial attached resources. |
vaultIds | readonly string[] | optional | Credential vaults to attach (vault_ids). |
metadata | Record<string, unknown> | optional | Arbitrary metadata. |
Example
const session = await client.createSession({
agentId: "agt_…",
environmentId: "env_…",
title: "Q3 report",
});
await client.sendMessage(session.id, "Summarize the attached filings.");Session events & steering
Sessions are driven by appending events. All of these POST to the session's events endpoint (sendMessage, interruptSession, confirmTool, respondCustomTool, and defineOutcome are convenience wrappers over sendEvents).
confirmToolanswers analways_asktool call —{ toolUseId, result: "allow" | "deny", denyMessage?, sessionThreadId? }.respondCustomToolanswers a custom tool call —{ toolUseId, content, isError?, sessionThreadId? }.defineOutcomestarts a rubric-graded run (iterate → grade → revise). Thedescription*is* the task — do not also send auser.message.listEventsreturns the full event history for lossless reconnect/dedupe.openEventStreamreturns the raw SSE body (used by the session driver); preferstreamRun/streamSessionfor canonical run-stream events.
Request body / Parameters
DefineOutcomeParams:
| Field | Type | Required | Description | |
|---|---|---|---|---|
description | string | required | The task the agent works toward. | |
rubric | `{ type: "text"; content } \ | { type: "file"; file_id }` | yes | Grading rubric. |
maxIterations | number | optional | Default 3, max 20. |
Example
await client.defineOutcome(session.id, {
description: "Produce a one-page competitive brief.",
rubric: { type: "text", content: "Covers pricing, positioning, and 3 risks." },
maxIterations: 5,
});Session resources
Files and GitHub repos attached to a live session.
updateResource is used e.g. to rotate a GitHub repo's authorization_token on a running session.
Multiagent threads
Per-subagent event streams inside a coordinator session (the primary thread is included).
Memory stores
Workspace-scoped persistent memory, with an immutable per-mutation version audit trail.
createMemory returns 409 memory_path_conflict_error if the path is occupied. updateMemory accepts an optional precondition: { type: "content_sha256", content_sha256 } for optimistic concurrency (the endpoint is POST, not PATCH). redactMemoryVersion scrubs a version's content while preserving the audit trail (leaked secrets / PII).
Request body / Parameters
createMemory (CreateMemoryParams): { path: string; content: string }. updateMemory (UpdateMemoryParams): { content?; path?; precondition? }. listMemories (ListMemoriesParams, query): { pathPrefix?; depth?; view?: "basic" | "full" }.
Vaults & credentials
Per-end-user MCP credential collections, attached to sessions via vault_ids.
createCredential takes auth as mcp_oauth (with optional refresh config) or static_bearer; one active credential per mcp_server_url. validateMcpOauthCredential returns a validation object with a status. Archiving a vault cascades to its credentials (secrets purged, records retained).
Deployments
Scheduled deployments fire a session on a recurring cron schedule; each firing writes a deployment-run record. Deployments have no retrieve-by-id or list endpoint — only the lifecycle actions below plus the run records.
runDeployment triggers a manual run immediately (works even while paused). listDeploymentRuns filters by deployment_id (query) and, with hasError, to failed runs only. pause/unpause toggle scheduled triggers with no backfill; archive is terminal.
Request body / Parameters
CreateManagedAgentDeploymentParams:
| Field | Type | Required | Description |
|---|---|---|---|
name | string | required | Deployment name. |
agentId | string | required | Agent each firing runs. |
environmentId | string | required | Environment to provision. |
initialEvents | readonly ManagedAgentEvent[] | required | Kickoff events; must include the starting user.message. |
schedule | { type: "cron"; expression; timezone } | required | IANA timezone; minute-level granularity. |
agentVersion | number | optional | Pin an agent version; omit for latest at each firing. |
resources | Record<string, unknown>[] | optional | Attached resources. |
vaultIds | readonly string[] | optional | Credential vaults. |
metadata | Record<string, unknown> | optional | Arbitrary metadata. |
Example
const deployment = await client.createDeployment({
name: "daily-standup",
agentId: "agt_…",
environmentId: "env_…",
initialEvents: [{ type: "user.message", content: "Summarize yesterday's PRs." }],
schedule: { type: "cron", expression: "0 9 * * *", timezone: "America/New_York" },
});
// inspect deployment.schedule.upcoming_runs_at to confirm the cron parsedRuntimeClient contract
Managed Agents implements the universal RuntimeClient surface by mapping runs onto sessions. Field-level run/stream semantics are documented under runtime operations — the notes here are Managed-Agents specifics.
startRuncreates a session (against the resolved default or per-run
metadata.agent_id/metadata.environment_id) and sends the kickoff message; it returns the session id as run_id with status started (Managed Agents is asynchronous). body.model/body.instructions are not applied — model and system prompt live on the agent.
getRunpolls the session and maps its status to aRuntimeRunState.cancelRuninterrupts the session gracefully (the session stays reusable).streamRunopens the SSE stream *before* sending the kickoff (stream-first
ordering, so no early event is missed) and emits canonical run-stream events.
streamSessionstreams an existing session without sending a kickoff.
Example
await client.streamRun(
{ input: "Draft the release notes.", metadata: { agent_id: "agt_…", environment_id: "env_…" } },
{ onEvent: (e) => console.log(e.type), onError: (err) => console.error(err) },
);Webhooks
Verify Managed Agents webhook deliveries (Standard Webhooks scheme) and parse the typed payload. Payloads are thin — fetch the resource by data.id for current state.
Pass the raw request body bytes as a string — re-serialized JSON changes the bytes and breaks the MAC. verifyManagedAgentWebhook reads standard webhook-id / webhook-timestamp / webhook-signature headers (with svix-* / x-webhook-* aliases) and throws WebhookVerificationError on a missing header, an out-of-tolerance timestamp (default 5 minutes, options.toleranceSeconds), or no matching signature. MANAGED_AGENT_WEBHOOK_EVENT_TYPES enumerates the data.type values Anthropic emits (session.*, agent.*, deployment.*, deployment_run.*, vault.*, vault_credential.*).
Example
import { verifyManagedAgentWebhook } from "@cavi-ai/api-client/providers/claude";
const event = await verifyManagedAgentWebhook(rawBody, req.headers, process.env.WEBHOOK_SECRET);
if (event.data.type === "deployment_run.failed") {
const run = await client.getDeploymentRun(event.data.id);
}Team provisioning
Map a TeamManifest onto Managed Agents: one coordinator agent plus one agent per roster member per team.
buildManagedAgentTeamsPlan is pure — it turns the manifest into a provisioning plan (coordinator spec + member specs; the coordinator's multiagent roster is left empty because member agent ids don't exist yet). provisionManagedAgentTeams executes it: it creates each member agent, then creates the coordinator referencing those member ids in its multiagent roster, and returns the created agent ids. Persist them and reference by id on createSession — do not re-provision per run.
Example
import { provisionManagedAgentTeams } from "@cavi-ai/api-client/providers/claude";
const { teams } = await provisionManagedAgentTeams(client, manifest);
// teams[0].coordinatorAgentId, teams[0].members[i].agentId