Skip to content

Claude Managed Agents compatibility

OpenMA implements a Claude Managed Agents (CMA)–compatible control-plane API and the Environment Worker protocol boundary on top of it. It is not Anthropic’s hosted CMA product: “compatible” here means protocol and wire-shape compatibility, not feature parity with the hosted service.

Two properties keep that claim honest:

  • Request and response contracts in packages/managed-agents-api/src/contracts/ are Zod schemas checked with satisfies against the official @anthropic-ai/sdk@0.120.0 beta/* resource types. When the SDK’s Managed Agents surface changes, the contracts stop compiling until OpenMA catches up.
  • Every endpoint below exists in packages/managed-agents-api/src/routes/ and is mounted on both hosts — the Cloudflare Worker (apps/main) and the Node server (apps/main-node) — under /v1. OpenMA-specific extensions live under /v1/oma/* so the compatible surface stays clean.

The Anthropic SDK sends the required anthropic-beta flags automatically. When calling endpoints by hand, send the flag listed in the matrix (for example anthropic-beta: managed-agents-2026-04-01) or the route rejects the request.

CMA surfaceOpenMAWhere in code
Agents CRUD + versioningImplemented — create, list, get, update, delete, archive, versions list + getroutes/agents.ts → /v1/agents
SessionsImplemented — create, list, get, update, delete, archiveroutes/sessions.ts → /v1/sessions
Session eventsImplemented — POST /events, GET /events (JSON), GET /events/stream (SSE)routes/session-events.ts
Session resourcesImplemented — attach, list, update, remove file, github_repository, memory_storeroutes/session-resources.ts
Session threadsImplemented — list, get, archive; thread events + stream live on the same routerroutes/session-threads.ts (/:sessionId/threads/:threadId/events, /stream)
Session outputsImplemented (OpenMA extension) — GET /outputs, GET /outputs/:filenameroutes/session-outputs.ts
EnvironmentsImplemented — CRUD + archiveroutes/environments.ts → /v1/environments
Environment Work (worker API)Implemented — list, work/poll, get, update, ack, heartbeat, stop, work/statsroutes/environment-work.ts → /v1/environments/:id/work
Memory storesImplemented — store CRUD + archive, memories CRUD with CAS precondition, memory_versions history + redactroutes/memory-stores.ts, memories.ts, memory-versions.ts → /v1/memory_stores (anthropic-beta: agent-memory-2026-07-22)
SkillsImplemented — skill CRUD, versions, version content downloadroutes/skills.ts, skill-versions.ts → /v1/skills (anthropic-beta: skills-2025-10-02)
FilesImplemented — upload, retrieve, content, deleteroutes/files.ts → /v1/files (anthropic-beta: files-api-2025-04-14)
Vaults & credentialsImplemented — vault CRUD + archive, credential CRUD + archive, mcp_oauth_validateroutes/vaults.ts, credentials.ts → /v1/vaults
ModelsImplemented (read-only) — list + retrieveroutes/models.ts → /v1/models; projected from tenant Model Cards
Deployments & deployment runsImplemented — deployment CRUD, run, run list/getroutes/deployments.ts, deployment-runs.ts → /v1/deployments, /v1/deployment_runs
DreamsImplemented — anthropic-beta: dreaming-2026-04-21routes/dreams.ts → /v1/dreams
MCP tunnelsImplemented — tunnel CRUD, certificates, reveal_token, rotate_tokenroutes/tunnels.ts, tunnel-certificates.ts → /v1/tunnels (anthropic-beta: mcp-tunnels-2026-06-22)
User profilesImplemented — anthropic-beta: user-profiles-2026-08-18routes/user-profiles.ts → /v1/user_profiles
MCP serversImplemented — as the mcp_servers field on Agent config (URL/HTTP/SSE/stdio); there is no standalone MCP-server resource in the SDK surfaceroutes/agents.ts, contracts/agent-input-components.ts
Callable agentsImplemented — callable_agents on Agent config derives call_agent_* toolsroutes/agents.ts, contracts/agents.ts; OMA integrations (GitHub/Slack/Linear/Feishu) are an extension under /v1/oma/integrations/*
/openai/v1 adapterImplemented — OpenAI Agents API projection over the same native sessionspackages/openai-agents-api, openai-agents-compat; mounted at /openai in apps/main/src/index.ts

POST /v1/sessions/:id/events accepts the official sendable union (sendableEventSchema in contracts/session-event-inputs.ts):

EventDirection
user.messageUser turn (text or file blocks). Mid-run: steers into the current turn at the next model/tool boundary (not a next-turn outbox).
user.interruptInterrupt a running turn, optionally scoped to session_thread_id. After interrupt, a following user.message starts a new turn.
user.tool_confirmationallow/deny a permission-gated tool call
user.custom_tool_resultResult for a custom tool that paused the session
user.tool_resultResult for a client-side tool call
user.define_outcomeOutcome rubric for evaluation (max_iterations ≤ 20)
system.messageSystem-injected text blocks

There is no separate POST .../pending/:id/steer extension. Ordinary mid-run user.message is steer. The DO-local pending_events table has been retired: queued mid-turn inputs live as unstamped rows in the append-only events log (processed_at null until consumed). See docs/implementation/cma-steer-coherent-turn.md.

These behaviors are part of the CMA wire contract OpenMA follows. Details also live under API Endpoints → Session budgets.

requires_action and budget_reached are settle-only

Section titled “requires_action and budget_reached are settle-only”

While the session (or thread) is idle with stop_reason: {"type": "requires_action"} (outstanding event_ids) or {"type": "budget_reached"}, only settle events are accepted:

  • user.tool_confirmation
  • user.tool_result
  • user.custom_tool_result
  • user.interrupt (accepted; ignored for budget pauses)

A user.message in that state returns 400 invalid_request_error. It is not deferred or queued. requires_action outranks budget_reached when both could apply.

A session may carry {"budget": {"type": "limit", "max_list_cost": {"amount": "500", "currency": "USD"}}} (amount in whole USD cents, integer string). The budget is active only when the session has max_list_cost and its model card has pricing. Crossing the cap emits idle/usage with stop_reason: budget_reached. PATCH rules (create-only add, raise-only max_list_cost, one-way null clear) are documented on the API reference page.

While retry_status: retrying / session.status_rescheduled, mid-turn inputs are held (not flushed, not started as a new turn). On retries_exhausted, unprocessed mid-turn rows are retained and advance with the next round of events (deliberate divergence from SDK wording that says queued inputs are flushed).

The sandbox/worker boundary follows the invariants in docs/claude-managed-agents-sandbox-provider-audit.md:

  • An Environment is a Work queue. A worker polls GET /v1/environments/{id}/work/poll continuously, or is woken by a signed webhook (session.status_run_started, Standard Webhooks signature, at most three attempts). The webhook is a hint only — Work is claimed by the poll, never by webhook delivery.
  • One credential path. Environment-scoped oma_env_ service keys are Bearer-only, valid only on that Environment’s /work/** routes, rotatable by create/revoke, and never authorize Session resources. Every claimed Work carries a separate claim-bound sessions_token (delivered inside ANTHROPIC_WORK_SECRET); a reclaim rotates it and fences the old owner.
  • ACK/heartbeat/stop ownership is explicit. Three embedded activation strategies exist: claim_then_acquire, poll_unacked_then_dispatch, and acquire_then_session_poll.
  • The event log is the source of truth. Work and webhook payloads carry only the Session ID — the worker then fetches the claimed Session with its sessions_token and replays the append-only log.
  • Credentials are designed to stay out of the sandbox. Enforced transparent HTTPS interception exists only on the cloudflare managed-runtime driver; everywhere else the boundary keeps secrets out of the session but does not claim network enforcement — the oma-vault env proxy on Node/Docker is advisory, and other drivers report unsupported.
  • External workers are a first-class lane. external_worker mode exposes the unchanged Work API and starts no OpenMA sandbox.

Per MANAGED_RUNTIME_PROVIDER_CONFORMANCE in packages/managed-runtime-host/src/provider-conformance.ts every managed-runtime driver supports the ama_worker lane. Most also support openma_supervised:

Provider driverama_workeropenma_supervised
node-dockeryesyes
cloudflareyesyes
cloudflare-bridgeyesno
e2byesyes
daytonayesyes
liteboxyesyes
boxrunyesyes
blaxelyesyes
spritesyesyes
vercelyesno
modalyesyes
superserveyesyes

OpenSandbox ships as packages/sandbox-adapter-opensandbox and is not yet a row in that managed-runtime conformance table. Per-driver lifecycle/workspace capabilities live in the same file — it is the machine-readable source of truth; the Managed Runtime Host page narrates it.

  • Warm pools — planned. No portable warm-pool surface exists. The one real binding is the GKE dispatch Port’s warmPool field (packages/environment-dispatch-gke).
  • fork — planned. There is no session/sandbox fork surface.
  • General egress allowlist proxy — planned (tracked in #297). Environment.config.networking type: "limited" with allowed_hosts is enforced only inside the web_fetch tool. bash/curl, web_search, and MCP calls are not restricted by it.
  • Credential egress is enforced on one driver. Only managed-runtime-cloudflare declares enforced live HTTPS interception. Every other driver reports unsupported unless the operator injects a certified implementation via options.credentialEgress.
  • No process-memory checkpoint/restore. runtimeCheckpoints is [] for every driver in the conformance table. Provider pause/suspend primitives appear as retained_runtime workspace strategies; they are not portable OpenMA checkpoints.
  • The stateless Messages API. /v1/messages is the core Anthropic API, not a Managed Agents surface, and OpenMA does not implement it.
  • Beta flags are enforced per resource family. Requests missing the documented anthropic-beta value get a 400 (requireBeta in packages/managed-agents-api/src/beta.ts).
  • Session list filters. metadata_key / metadata_value query params are rejected with a 400 — list filters cover agent_id, agent_version, created_at bounds, deployment_id, memory_store_id, statuses[], order, and include_archived.
  • Exhausted mid-turn retain. On retries_exhausted, OpenMA retains unprocessed mid-turn inputs instead of flushing them (see steer doc above).
  • OpenAI adapter limits. The /openai/v1 adapter supports sandbox-free sessions (environment: { "type": "none" }) but does not accept advanced environment configuration, plugins, or runtime file operations. Hosted OpenAI dynamic sub-agent control is unavailable (the Node runtime provides it). Details: docs/openai-agents-compatibility-status.md.
  • OMA extensions share the host. /v1/oma/* (API keys, me, tenants, integrations, Matter/Loops, evals, stats, MCP/inference proxies) and convenience routes like POST /v1/sessions/:id/messages are OpenMA additions, not CMA surface — see API Endpoints.