Skip to content

CLI & SDK

openma is callable via two routes:

  • @openma/cli — a Node CLI for scripting against the API.
  • Anthropic’s official SDKs — TypeScript (@anthropic-ai/sdk), Python (anthropic), and Go (anthropic-sdk-go) — pointed at openma’s baseURL. The openma API is wire-compatible with the Claude Managed Agents API, so the same SDKs work against both.

The CLI lives in this repo (packages/cli).

Terminal window
npm i -g @openma/cli

The CLI reads two env vars:

Terminal window
export OMA_BASE_URL="https://app.openma.dev"
export OMA_API_KEY="oma_..."

For self-hosters, point OMA_BASE_URL at your own deployment.

Terminal window
# List agents
oma agents list
# Create an agent from a JSON file
oma agents create -f ./my-agent.json
# Start a session
oma sessions create --agent agent_abc123 --env env_abc123 --title "research"
# Chat — post a turn AND stream the reply token-by-token to your terminal
oma sessions chat sess_xyz "Summarize the openma docs."
# Fire-and-forget message (no streaming back; useful when something else is tailing)
oma sessions message sess_xyz "Now also include the integrations."
# Tail a session's full event stream (SSE under the hood)
oma sessions tail sess_xyz
# Replay a session's full event log
oma sessions logs sess_xyz
# Show your usage
oma usage

Run oma --help or oma <command> --help for the full surface.

The openma API is wire-compatible with the Claude Managed Agents API. Use an Anthropic SDK directly for the standard surface. TypeScript applications that also use OpenMA extensions can use @openma/sdk: it composes the official client, adds strong extension types, and does not reimplement requests, pagination, streaming, or errors.

Terminal window
npm i @openma/sdk
# or: pnpm add @openma/sdk
# or: bun add @openma/sdk
import { OpenMA } from "@openma/sdk";
const client = new OpenMA({
baseURL: "https://app.openma.dev",
apiKey: process.env.OMA_API_KEY!,
});
// Create an environment.
const env = await client.beta.environments.create({
name: "my-env",
config: {
type: "cloud",
networking: { type: "unrestricted" },
packages: { type: "packages", pip: ["pandas"] },
},
});
// List sessions.
for await (const sess of client.beta.sessions.list({ limit: 10 })) {
console.log(sess.id, sess.title);
}

Every namespace in the official SDK works against openma:

NamespaceNotes
client.beta.environmentslist, create, retrieve, update, archive, delete
client.beta.agentslist, create, retrieve, update, archive, plus versions sub-resource
client.beta.sessionslist, create, retrieve, update, archive, delete, plus events, resources, threads sub-resources
client.beta.vaultslist, create, retrieve, update, archive, delete, plus credentials sub-resource
client.beta.memoryStoreslist, create, retrieve, archive, delete, plus memories, memoryVersions sub-resources (update not yet implemented server-side)
client.beta.filesfull list/upload/retrieve/delete
client.beta.skillsfull coverage
client.beta.modelsfull coverage

Endpoints that don’t exist in Anthropic’s API (tenants, OAuth, integrations, evals, cost reports, runtimes, …) live under /v1/oma/*. @openma/sdk provides typed resources for supported extensions and a path-guarded fallback for the rest:

const usage = await client.oma.request<{
total_cost: number | null;
total_estimated_cost: number | null;
scope: { type: string; id: string };
attribution: {
provider: string;
source: 'provider_billed' | 'provider_metered' | 'openma_metered' | 'estimated';
granularity: string;
is_invoice_grade: boolean;
data_completeness: 'complete' | 'partial' | 'unavailable';
warnings: string[];
};
}>({
method: "get",
path: "/v1/oma/cost_report",
});

Cost reports are explicit about their evidence. total_cost is only populated when a provider reports monetary cost; price-card math stays in total_estimated_cost. A missing provider dataset is partial or unavailable, never a zero. The current Cloudflare adapter is account-scoped, so this endpoint does not claim per-tenant or per-session Cloudflare invoice attribution. The account-scoped report and its /pricing sub-resource require the deployment’s operator API_KEY; tenant-scoped API keys and Console cookies are rejected.

CostAttributionPort is the reusable server-side extension point. The shipped Cloudflare and Vercel adapters retain provider FOCUS payloads; Blaxel and Modal retain their provider-native rows while returning the same scope and attribution envelope. The OpenMA runtime-ledger adapter supports tenant and session allocation using a versioned operator rate card. Provider-specific credentials are constructor inputs to their adapter package and are never accepted in a report query or returned in a report.

Server compositions can pass an AbortSignal with a report query. All shipped adapters propagate cancellation to their provider I/O and normalize transport, SDK, authorization, scope, response, and pagination failures into stable CostAttributionError codes. The runtime adapter connects to the platform ledger with createRuntimeUsageReader(services.usage) and reads billed as well as unbilled history; acknowledgement is a billing workflow state, not deletion.

Or use plain fetch — every OMA endpoint is JSON, no extra runtime needed.

A small set of OMA-specific agent fields (auxiliary model, harness selector, sandbox runtime binding) are passed under an _oma: body field on Agent create / update calls. OpenMA reads them; do not send _oma to an Anthropic endpoint. They are optional — agents that omit _oma use the vanilla Claude Managed Agents shape.

Provider-specific inference options are JSON and stay on the model they affect:

await client.beta.agents.update("agent_abc123", {
model: {
id: "deepseek-chat",
provider_options: {
pi: { reasoning: "off", samplingParams: { temperature: 0 } },
},
},
_oma: {
aux_model: {
id: "deepseek-chat",
provider_options: { pi: { reasoning: "off" } },
},
},
});

The main model options reach main-turn and compaction calls. Auxiliary options reach tool-internal model calls such as web_fetch summarization. Keep custom Agent extensions under _oma; arbitrary root fields are rejected.

FieldPurpose
aux_modelAuxiliary model used for tool-internal LLM work (e.g. web_fetch summarization). When unset, tools fall back to returning raw content.
appendable_promptsVersioned prompt IDs appended to the Agent system context.
harnessSelects the agent loop harness implementation. Reserved for OMA-managed runtimes.
acpProcess, restart, idle-timeout, and per-turn-timeout configuration used when harness is acp-sandbox. The command runs through the selected sandbox provider’s duplex-process Port.
runtime_bindingSelects which sandbox runtime services the agent’s sessions. Reserved for OMA-managed runtimes.

TypeScript gets strong request and response types from @openma/sdk. Python and Go continue to use their official SDK escape hatches:

import { OpenMA } from "@openma/sdk";
const client = new OpenMA({
baseURL: "https://app.openma.dev",
apiKey: process.env.OMA_API_KEY!,
});
const agent = await client.beta.agents.create({
name: "researcher",
model: { id: "claude-opus-4-7" },
_oma: {
aux_model: { id: "claude-haiku-4-5" },
appendable_prompts: ["prompt_review"],
},
});
console.log(agent._oma?.aux_model?.id);

The server returns Anthropic-standard error envelopes, so the SDK’s typed errors work as documented:

import Anthropic from "@anthropic-ai/sdk";
try {
await client.beta.sessions.retrieve("sess-bogus");
} catch (err) {
if (err instanceof Anthropic.APIError) {
console.error(err.status, err.error); // {error: {type, message}, request_id, type: "error"}
if (err.error?.error?.type === "not_found_error") { /* ... */ }
}
}