Configuration Reference
Authoritative reference for openma’s configuration shapes. The TypeScript source of truth lives in packages/api-types; the in-repo human-readable schema doc is AGENTS.md.
interface AgentConfig { id: string; // assigned by the platform name: string; model: string; // e.g. "claude-sonnet-4-6" system: string; // system prompt tools: ToolDefinition[]; skills?: string[]; // skill ids environment_id?: string; mcp_servers?: McpServerConfig[]; memory_store_id?: string; harness?: string; // defaults to "default" archived?: boolean; version: number; // platform-bumped on every update created_at: string; updated_at: string;}Pi harness compaction
Section titled “Pi harness compaction”harness: "pi" always enables harness-owned context compaction. These optional
metadata keys select and tune the built-in policy:
{ "harness": "pi", "metadata": { "compaction_strategy": "cc-style", "compaction_trigger_fraction": 0.75, "compaction_max_summary_tokens": 2000, "compaction_summary_prompt": "Optional replacement summary prompt" }}compaction_strategy accepts cc-style (default), opencode-style, or
summarize. Invalid names fall back to cc-style; there is intentionally no
none mode. All built-in policies use pi-ai without requesting reasoning.
cc-styleuses an isolated summary payload: a short summary-only system prompt, no tools, image placeholders instead of image bytes, and a separate compaction session id. This is the robust default and intentionally does not reuse the main agent’s provider prefix cache.opencode-stylehas the same isolated payload boundary, with a structured Goal / Instructions / Discoveries / Accomplished / Files summary template.summarizeis the explicit cache-aware strategy. It replays the main request’s exact system prompt, tool declarations, message history, and session id, then appends the private compaction instruction. It deliberately leavestoolChoiceunset because some provider adapters remove tools fortoolChoice: "none", which changes the cache prefix.completeSimpledoes not execute tools; a returned tool call rejects the compaction boundary. Prefix-caching providers can therefore reuse the warm conversation prefix.
Applications can replace the whole PiCompactionPolicy when constructing
PiHarness; OpenMA still owns the canonical boundary event, failure fallback,
and the single overflow retry. Every strategy replaces old history with a new
summary boundary, so the first main-model request after compaction is a new
prefix even when the summary call itself reused the old prefix.
Tool definitions
Section titled “Tool definitions”type ToolDefinition = | { type: 'agent_toolset_20260401' } // built-in toolset | { type: 'custom'; name: string; description: string; input_schema: JsonSchema; execution: | { type: 'http'; endpoint: string; method?: string } | { type: 'sandbox'; command: string }; };MCP server config
Section titled “MCP server config”interface McpServerConfig { name: string; // becomes the prefix: mcp__<name>__<tool> type: 'url' | 'stdio'; url?: string; // required by host-side OpenMA harnesses authorization_token?: string; // inline bearer (otherwise: matched vault credential) stdio?: { command: string; args?: string[]; env?: Record<string, string>; port: number; // 127.0.0.1:port the spawned process binds to ready_timeout_ms?: number; // default 60_000 };}stdio is an adapter-owned declaration. It works only when an external Worker or a harness running inside the sandbox owns the child process and transport. The built-in Node and Cloudflare host-side harnesses require url and reject stdio-only declarations, because sandbox-local 127.0.0.1 is not automatically reachable from the control plane. URL MCP discovery is bounded at 15 s per server; up to 20 servers per agent.
Environment
Section titled “Environment”interface EnvironmentConfig { id: string; name: string; base_image: string; // e.g. "openma/sandbox-base:python-3.12" packages: { pip?: string[]; npm?: string[]; apt?: string[]; cargo?: string[]; gem?: string[]; go?: string[]; }; network?: { allowlist?: string[]; // hostnames the sandbox may reach denylist?: string[]; }; env?: Record<string, string>;}A vault is a tenant-scoped credential bundle; credentials inside it are bound to MCP servers or sandbox CLIs.
interface Vault { id: string; tenant_id: string; name: string; created_at: string; updated_at: string; archived_at?: string;}
// Credentials live in a separate table; each is one of three typestype CredentialAuth = | { type: 'static_bearer'; token: string; mcp_server_url: string } | { type: 'mcp_oauth'; access_token: string; refresh_token?: string; token_endpoint?: string; expires_at?: string; mcp_server_url: string } | { type: 'cap_cli'; cli_id: string; token: string }; // e.g. cli_id: "gh", "glab", "aws"Binding to a host happens via the credential, not the vault: a static_bearer / mcp_oauth credential matches by parsing the request hostname against mcp_server_url; a cap_cli credential matches by cli_id lookup in the cap spec registry. Up to 20 credentials per vault. Tokens are AES-GCM-encrypted at rest under PLATFORM_ROOT_SECRET and never returned via the API once written.
Codex / ChatGPT subscription credentials
Section titled “Codex / ChatGPT subscription credentials”Current status: Model Cards currently accept static API-key credentials. Codex/ChatGPT subscription OAuth is a direct-host adapter contract for local debugging; it is not a hosted Model Card or a remote-sandbox credential type.
When that direct-host adapter is enabled, it keeps the provider OAuth credential
in the host’s local Pi-compatible credentials store. It must never copy
~/.codex/auth.json, a refresh token, or an access token into a sandbox,
workspace checkpoint, output archive, Model Card, or request payload. The
sandbox receives no subscription credential and cannot refresh it.
For a remote or managed sandbox, configure an approved API key or provider OAuth credential behind the OMA model gateway instead. The gateway is the credential boundary: it authenticates the request, applies the tenant/model policy, and returns only the model response to the sandbox. This preserves the same harness endpoint shape without turning a subscription login into a portable secret.
These paths are intentionally distinct:
| Path | Credential location | Intended use |
|---|---|---|
Local direct-host | Trusted host Pi credentials store | Local debugging only |
| Remote/managed sandbox | OMA model gateway; approved API/OAuth credential stays in the control plane | Production/managed execution |
Do not treat the local credential file as a portable session artifact. If it is missing after a runtime replacement, resume the ACP/native session from its declared session state and require the user to authenticate again on the trusted host; do not reconstruct or transmit the subscription token from Session events.
interface SkillMetadata { type: 'skill'; id: string; display_title: string; name: string; // SKILL.md frontmatter `name` — also the mount-folder name description: string; source: 'anthropic' | 'custom'; latest_version: string; // numeric epoch string created_at: string; updated_at: string;}
// Per-version detail returned by GET /v1/skills/:id/versions/:versioninterface SkillVersion { version: string; files: Array<{ filename: string }>; // R2 objects under t/{tenant}/skills/{id}/{version}/<filename>}The platform mounts skill files at /home/user/.skills/{name}/ (using the SKILL.md name, not id) and inlines the SKILL.md body directly into the system prompt at session start — no lazy read. The injected wrapping is:
<source name="skill:{id}"><skill name="{name}">{full SKILL.md body}</skill></source>Attach to an agent with the object form (not a bare string array):
{ "skills": [{ "skill_id": "skill_abc123", "type": "custom" }] }Built-in skills (source: "anthropic"): xlsx, pdf, docx, pptx — four total, no upload needed.
Memory store
Section titled “Memory store”interface MemoryStore { id: string; agent_id: string; embedding_model: string; // defaults to platform setting vector_index: string; // Vectorize index name}Session
Section titled “Session”interface SessionMeta { id: string; agent_id: string; agent_version: number; // pinned at creation status: 'pending' | 'running' | 'idle' | 'done' | 'failed'; created_at: string; updated_at: string;}
interface SessionEvent { id: string; session_id: string; type: string; // 'agent.message', 'agent.tool_use', 'agent.thinking', etc. data: unknown; created_at: string;}The full event type catalog is in packages/api-types/src/events.ts.
Environment variables
Section titled “Environment variables”Required for self-host. Set as Worker secrets via npx wrangler secret put NAME.
Required
Section titled “Required”| Variable | Worker | Purpose |
|---|---|---|
PLATFORM_ROOT_SECRET | main, integrations | Root secret for at-rest encryption (credentials, model card keys, integration tokens) and outbound MCP token signing. Workers refuse to start without it. Back it up — losing it makes every encrypted row unreadable. |
BETTER_AUTH_SECRET | main | better-auth session signing key |
API_KEY | main | Initial dev API key for the REST API |
INTEGRATIONS_INTERNAL_SECRET | main, integrations | Shared secret between main and integrations workers |
Optional integrations
Section titled “Optional integrations”| Variable | Worker | Purpose |
|---|---|---|
ANTHROPIC_API_KEY | main, agent | Fallback LLM credential when a tenant has not added a Model Card. In production, prefer per-tenant Model Cards from the Console — they’re encrypted under PLATFORM_ROOT_SECRET and rotatable without redeploy. (Alternates: OPENAI_API_KEY, MINIMAX_API_KEY.) |
LINEAR_CLIENT_ID | integrations | Linear OAuth |
LINEAR_CLIENT_SECRET | integrations | Linear OAuth |
LINEAR_WEBHOOK_SECRET | integrations | Verify inbound Linear webhooks |
GITHUB_APP_ID | integrations | GitHub App ID |
GITHUB_PRIVATE_KEY | integrations | GitHub App private key (.pem contents) |
GITHUB_WEBHOOK_SECRET | integrations | Verify inbound GitHub webhooks |
SLACK_CLIENT_ID | integrations | Slack OAuth |
SLACK_CLIENT_SECRET | integrations | Slack OAuth |
SLACK_SIGNING_SECRET | integrations | Verify inbound Slack events |
GOOGLE_CLIENT_ID | main | Google sign-in for Console |
GOOGLE_CLIENT_SECRET | main | Google sign-in for Console |
Optional infra
Section titled “Optional infra”| Variable | Worker | Purpose |
|---|---|---|
TAVILY_API_KEY | main, agent | Web search backend for web_search built-in |
CLOUDFLARE_API_TOKEN | main | Programmatic CF resource management (optional) |
CLOUDFLARE_ACCOUNT_ID | main | Programmatic CF resource management (optional) |
INTEGRATIONS_PUBLIC_URL | main | Override auto-detected integrations URL |
PER_TENANT_DB_ENABLED | main | Set "true" to enable per-tenant D1 isolation |
STORE_BACKENDS | main | JSON config for storage backends (advanced) |
DATABASE_URL | main | External Postgres URL (advanced) |
Cloudflare bindings
Section titled “Cloudflare bindings”What each Worker needs in its wrangler.jsonc:
apps/main
Section titled “apps/main”| Binding | Type | Name |
|---|---|---|
MAIN_DB | D1 | openma-auth |
CONFIG_KV | KV | (your namespace) |
FILES_BUCKET | R2 | managed-agents-files |
AI | Workers AI | (built-in) |
VECTORIZE | Vectorize | openma-memory |
SANDBOX_sandbox_default | Service | → agent worker |
INTEGRATIONS | Service | → integrations worker |
SEND_EMAIL | (your sender) | |
ANALYTICS | Analytics Engine | oma_events |
apps/agent
Section titled “apps/agent”| Binding | Type | Name |
|---|---|---|
SESSION_DO | Durable Object | SessionDO |
SANDBOX | Durable Object | Sandbox (Container class) |
CONFIG_KV | KV | (shared with main) |
MAIN_DB | D1 | (shared with main) |
WORKSPACE_BUCKET | R2 | managed-agents-workspace |
FILES_BUCKET | R2 | (shared with main) |
AI, VECTORIZE, BROWSER, ANALYTICS | (same as main) |
apps/integrations
Section titled “apps/integrations”| Binding | Type | Name |
|---|---|---|
MAIN_DB | D1 | (shared with main) |
MAIN | Service | → main worker |