The agents.yaml schema. Every field below is sourced from the Zod schema in packages/sdk/src/internal/parser/schema.ts and the config types in packages/sdk/src/internal/types/config.ts. For a tutorial, see Configure an agent.
version: "1"providers: { <name>: <provider-config> }defaults:
provider: <name> | "all"identity: <identity-name>environments: { <name>: EnvironmentDecl }tunnels: { <name>: TunnelDecl }vaults: { <name>: VaultDecl }memory_stores:{ <name>: MemoryStoreDecl }skills: { <name>: SkillDecl }files: { <name>: FileDecl }identities: { <name>: IdentityDecl }agents: { <name>: AgentDecl }channels: { <name>: ChannelDecl }deployments: { <name>: DeploymentDecl }| Field | Type | Required | Description |
|---|---|---|---|
version | string | yes | Schema version. Currently "1". |
providers | map | yes | One block per provider; each holds its credentials. |
defaults.provider | string | no | Default target for plan/apply. all targets every declared provider. |
defaults.identity | string | no | Logical name of the default declared Identity used by identity-aware resources and Forward Sessions. |
environments | map | no | Cloud runtimes. |
tunnels | map | no | Existing Qoder BYOC tunnels referenced by sessions; OpenCMA does not manage their lifecycle. |
vaults | map | no | Credential stores. |
memory_stores | map | no | Persistent agent context (Qoder, Volcengine Ark). |
skills | map | no | Reusable capability modules. |
files | map | no | Local files uploaded to the Files API (Bailian, Volcengine Ark). |
identities | map | no | Stable end-user identities. Provider support is capability-gated. |
agents | map | no | The core managed-agent resources. |
channels | map | no | External messaging channels bound to an Identity and Agent. Provider support is capability-gated. |
deployments | map | no | Repeatable run units. |
Secrets use ${VAR_NAME} and resolve from .env (walking up to the project root). agents init appends agents.state.json and .env to .gitignore.
Managed identities use the integrating product's stable end-user id:
identities:
chen:
provider: qoderexternal_id: user_456name: Chenenabled: truemetadata:
department: engineeringagents apply creates or updates the remote Identity and stores its provider id in state. To reference an Identity managed outside this project, use the mutually exclusive external-reference form:
identities:
chen:
provider: qoderidentity_id: idn_019eabc123External references are verified and recorded but never updated or deleted.
channels:
support-dingtalk:
provider: qoder # optional; inherits defaults.provideragent: support-agent # required for mode: fixed; ignored for mode: pairingidentity: chen # optional; inherits defaults.identity. ignored for mode: pairingtype: dingtalkmode: fixed # optional; defaults to fixedname: Support DingTalk # optional; defaults to the YAML keyenabled: true # optional; defaults to truecredentials:
client_id: ${DINGTALK_CLIENT_ID}client_secret: ${DINGTALK_CLIENT_SECRET}options:
include_tool_calls: falseinclude_thinking: false| Field | Type | Required | Description |
|---|---|---|---|
provider | string | no | Provider name; inherits defaults.provider. |
agent | string | conditional | Logical Agent name. Required for fixed mode; ignored for pairing mode. |
identity | string | conditional | Logical Identity name; inherits defaults.identity. Required for fixed mode; ignored for pairing mode. |
type | string | yes | Provider-specific channel type. Qoder supports dingtalk, feishu, and wecom; wechat is QR-only. |
mode | fixed | pairing | no | fixed (default) binds the channel to one Identity/Template. pairing creates a transport-only channel for Schedules/Sinks. |
name | string | no | Display name; defaults to the YAML key. |
enabled | boolean | no | Defaults to true. |
credentials | map | conditional | Provider-specific credentials. Required for credential-based channel types. |
options | map | no | Provider-specific response options, e.g. include_tool_calls, include_thinking. |
The declaration intentionally uses logical agent and identity references. Provider adapters resolve remote ids and map type, credentials, and options to provider wire fields. Qoder Channels in fixed mode require the referenced Agent to use Forward delivery. pairing mode omits Identity/Template binding and is intended for Schedule sinks such as scheduled group broadcasts. Credential-based Qoder support currently covers DingTalk, Feishu, and WeCom; personal WeChat remains QR-only.
managed_tool_config declares the provider-operated tools an Agent Harness runs
itself, rather than tools the model calls through the sandbox. Schedule
management is the current use: enabling create_forward_schedule,
list_forward_schedules, and delete_forward_schedule lets an end user create
and cancel Schedules in natural language from a Web or IM Channel conversation.
enabled_tools replaces the provider's whole enabled set, so an empty array
turns every managed tool off. Omitting the field entirely sends nothing: because
Qoder Forward Template updates are merge-style, an undeclared field leaves
whatever the remote Template already had. Declare it whenever the tools matter —
a Template recreated from scratch (after a destroy, a manual deletion, or lost
state) otherwise comes back with no managed tools and no error.
Each provider under providers is validated by the provider's own config schema.
| Field | Type | Required | Description |
|---|---|---|---|
api_key | string | yes | DashScope API key. |
workspace_id | string | yes | Bailian workspace id (llm-...). |
base_url | string | no | Override the derived endpoint (https://<workspace_id>.cn-beijing.maas.aliyuncs.com/api/v1/agentstudio). |
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
api_key | string | yes | — | Qoder PAT. |
gateway | string | no | https://api.qoder.com/api/v1/cloud | Cloud gateway base URL. |
| Field | Type | Required | Description |
|---|---|---|---|
api_key | string | yes | Anthropic API key. |
beta | string | no | Optional anthropic-beta header value. |
| Field | Type | Required | Description |
|---|---|---|---|
api_key | string | yes | Volcengine Ark API key. |
environments:
<name>:
name: <string> # optionaldescription: <string> # optionalprovider: <string> # optional; pin to one providerenvironment_id: <string> # optional; reference an existing provider environment without managing itconfig:
type: cloud | self_hostednetworking: { ... }packages: { ... }setup_script: <string>metadata: { <key>: <string> }| Field | Type | Required | Description |
|---|---|---|---|
environment_id | string | no | Existing environment ID. When present, OpenCMA never creates, updates, or deletes the remote environment. Removing this line later is blocked as an ownership error — release first with agents state rm (see Use BYOC environments). |
config.type | "cloud" | "self_hosted" | yes | Environment type. self_hosted is used for Qoder BYOC. |
config.networking.type | "unrestricted" | "limited" | no | Network policy. |
config.networking.allow_mcp_servers | boolean | no | Allow outbound MCP. |
config.networking.allow_package_managers | boolean | no | Allow package managers. |
config.networking.allowed_hosts | string[] | no | Allow-list for limited networks. |
config.packages.apt | pip | npm | cargo | gem | go | string[] | no | Preinstalled packages. |
config.setup_script | string | no | Sandbox setup script. Qoder runs it with /bin/bash -lc after package installation; maximum UTF-8 size is 64 KB. Other providers currently reject this field. |
metadata | map<string,string> | no | Free-form metadata. |
Qoder accepts only apt, npm, and pip in package requests. Its API may return empty cargo, gem, and go arrays as reserved response fields, but declaring non-empty values for them is rejected locally. Setup scripts run while a new sandbox is prepared, time out after 10 minutes, and a non-zero exit prevents the Session from starting. Keep scripts idempotent and use vault-backed credentials instead of embedding secrets.
For a managed Qoder self_hosted environment, config accepts only type and optional setup_script; networking and packages belong to cloud environments. External environment_id references remain unmanaged.
tunnels:
internal-network:
tunnel_id: tnl_00xxxxTunnels are existing Qoder resources allocated by the BYOC administrator. They are passed only when a Qoder session/deployment is created and are never created, updated, or deleted by OpenCMA. See Use BYOC environments for a complete setup and lifecycle guide.
vaults:
<name>:
display_name: <string>provider: <string> # optionalcredentials: [ CredentialDecl ]metadata: { <key>: <string> }CredentialDecl is a discriminated union on type:
| Field | Type | Required | Description |
|---|---|---|---|
name | string | yes | Credential name. |
type | "static_bearer" | yes | |
mcp_server_url | string | yes | MCP server URL. |
access_token | string | yes | Bearer token (string or number, coerced). |
protocol | "sse" | "streamable_http" | no | MCP transport. |
| Field | Type | Required | Description |
|---|---|---|---|
name | string | yes | Credential name. |
type | "environment_variable" | yes | |
secret_name | string | yes | Secret name. |
secret_value | string | yes | Secret value (string or number, coerced). |
networking.type | "unrestricted" | "limited" | no |
memory_stores:
<name>:
description: <string>provider: <string> # optionalmetadata: { <string>: <string> } # optionalentries: [ { key: <string>, content: <string> } ]Supported on Qoder, Claude (beta), and Volcengine Ark (Bailian: unsupported).
Declarative entries are managed seeds: apply creates or updates those paths but
preserves additional memories written by agents. Runtime CRUD and version commands
are documented in examples/memory/.
skills:
<name>:
source: <string> # path to skill directorydescription: <string> # optionalversion: <string> # optionalorigin: "custom"| "official" # optionalprovider: <string> # optionalfiles:
<name>:
source: <string>name: <string> # optionalpurpose: <string> # optionalprovider: <string> # optionalagents:
<name>:
description: <string>model: <string> | { <provider>: <string> }instructions: <string> | <path>environment: <string>tunnel: <string> # optional; Qoder BYOC tunnel nameprovider: <string>tools: { builtin: [...], default_permission: allow, mcp: [...], permissions: {...} }mcp_servers: [ { name, type?, url? } ]skills: [ <string> | { type, skill_id, version? } ]vault: <string>memory_stores: [ <string> ]default_memory_store: # Qoder Forward only; requires defaults.identityname: <string> # 1-255 charactersdescription: <string> # optional; up to 1024 charactersdelete_on_destroy: <boolean> # optional; defaults to false (retain)environment_variables: { <key>: <string> } # Qoder onlymanaged_tool_config: { enabled_tools: [ <string> ] } # Qoder Forward delivery onlyresources: [ SessionResource ]multiagent: { type: "coordinator", agents: [...] }metadata: { <key>: <string> }Qoder creates one writable, system-managed Memory Store for an (Identity, Template) pair when its first Forward Session is created. default_memory_store lets OpenCMA manage the display metadata and destroy policy of that provider-created Store; it does not declare a second, ordinary entry under the top-level memory_stores collection.
defaults:
provider: qoderidentity: support-useridentities:
support-user:
external_id: support-user # managed by OpenCMAagents:
support:
# ...delivery:
qoder:
type: forwarddefault_memory_store:
name: "Support group memory"description: "Confirmed support knowledge and operating rules"delete_on_destroy: falseApply behavior:
- Requires Qoder Forward delivery and
defaults.identity. - Locates the Store mounted as
system_managed: trueandaccess: read_write, then idempotently updates itsnameand optionaldescription. - Does not create an initialization Session. Before the first real Session has created the Store, apply reports the reconciliation as pending. Run apply again after a Session exists.
namechanges the provider Store's display name, so it can be meaningful instead of remaining the provider-generated default.
Destroy behavior:
delete_on_destroy | Result |
|---|---|
omitted or false | Retain the Store, its Memories, and all version history. This is the default. |
true | Permanently delete the Store, its Memories, and all version history after its system mount has been removed. |
For permanent deletion, OpenCMA captures and persists the Store ID before archiving the Template and deleting the Identity. Qoder may remove the system mount asynchronously, so OpenCMA uses bounded retries for a still mounted conflict. If the conflict remains, it tries archive → delete, matching the lifecycle verified against the live Qoder service. A cleanup that still cannot finish is retained in state and reported as a partial destroy; a later agents destroy resumes it even when all ordinary resources are already gone.
If the preflight cannot resolve the Identity, Template, or Store lookup, destroy aborts before deleting any project resource. Authentication, permission, and non-retryable validation errors fail immediately. --cascade does not override this field.
delete_on_destroy: true requires an OpenCMA-managed Identity. An external identity_id is never deleted by OpenCMA, so it keeps the system Store mounted and fails configuration validation. Permanent deletion is irreversible; keep the default false unless data removal is explicitly required.
| Field | Type | Required | Description |
|---|---|---|---|
model | string | map<provider,string> | yes | Single model or a per-provider map. |
instructions | string | yes | Inline text or a path to a file (resolved relative to the config). |
environment | string | no | Environment name. |
tunnel | string | no | Qoder BYOC tunnel name from tunnels; unsupported for other providers. |
provider | string | no | Pin the agent to one provider. |
tools.builtin | string[] | yes (in tools) | Lowercase tool names. |
tools.default_permission | "allow" | "ask" | no | Permission inherited by enabled builtins; defaults to allow. |
tools.permissions | map<string,"allow"|"ask"> | no | Case- and separator-insensitive overrides for enabled builtins. Unknown and duplicate normalized names are rejected. |
tools.mcp[] | McpToolkitDecl[] | no | Select tools from an official MCP server. |
mcp_servers[] | { name, type?, url? } | no | URL (url/http) or official MCP server. |
skills[] | string | AgentSkillRef | no | Skill name or { type: "official"|"custom", skill_id, version? }. |
vault | string | no | Vault name. |
files | string[] | no | File declarations inherited by a Qoder Forward Template. These files are created through the Forward File API. |
memory_stores | string[] | no | Bound memory stores. |
default_memory_store.name | string | yes (with default_memory_store) | Display name for Qoder Forward's writable system-managed Store; 1–255 characters. |
default_memory_store.description | string | no | Display description for the system-managed Store; up to 1024 characters. |
default_memory_store.delete_on_destroy | boolean | no | Permanently delete the Store during destroy. Defaults to false (retain). |
environment_variables | map<string,string> | no | Qoder runtime variables. Managed Sessions use Qoder's KEY=VALUE;... wire format; Forward Templates store the map as defaults and Forward Sessions send it under config.environment_variables. |
managed_tool_config.enabled_tools | string[] | no | Provider-operated tools the Agent Harness exposes, e.g. create_forward_schedule, list_forward_schedules, delete_forward_schedule. Qoder Forward delivery only; declaring it on managed delivery is a validation error. |
resources | SessionResource[] | no | Resources attached to every managed Session created for the Agent. |
multiagent.type | "coordinator" | no | Declare a coordinator agent. |
multiagent.agents | string[] | yes (with multiagent) | Agents it orchestrates. |
metadata | map<string,string> | no | Free-form metadata. |
For Qoder Forward delivery, a locally declared Environment is created only through the Forward Environment API. An
external environment_id may reference an Environment from either the Managed API or the Forward API; OpenCMA does
not create or mutate such a reference and resolves its API domain when checking existence. Referenced custom Skills, Vaults and Credentials, Files, and explicit
Memory Stores are created through their Forward APIs. A locally managed Environment, Skill, Vault, File, or Memory
Store cannot be shared by Managed and Forward Agents under one logical declaration; declare separate resources for
the two API domains. Explicit Forward Memory Stores require defaults.identity and are mounted read-only to that
Identity and Template.
Qoder and Claude managed Sessions support a provider-neutral GitHub repository resource:
agents:
reviewer:
# ...resources:
- type: github_repositoryurl: https://github.com/acme/private-repo.gitauthorization_token: ${GITHUB_TOKEN}checkout: { branch: main } # or: { commit: <full-sha> }mount_path: /data/workspace/private-repo # optionalKeep authorization_token in .env; never put its value directly in agents.yaml. Qoder mount paths must start with /data/. If the field is omitted for Qoder, OpenAgentPack sends /data/workspace/<repo-name> automatically; for the URL above that is /data/workspace/private-repo. Qoder requires this /data path for the repository mount to take effect. Other providers retain their own path semantics.
Mount roots are provider invariants: Qoder uses /data, Claude uses /workspace, and Bailian and Ark use /mnt. A relative uploaded-file path is resolved under the target root. An explicit absolute path must already use the matching root and is passed through unchanged; OpenAgentPack rejects mismatched absolute paths instead of silently rewriting them. For GitHub Session resources, when the path is omitted Qoder derives /data/workspace/<repo-name> and Claude derives /workspace/<repo-name>.
tools:
mcp:
- type: mcp_toolkitmcp_server_name: WebSearch # mcpServerName also accepteddefault_config: { enabled: false } # defaultConfig also acceptedconfigs:
- name: bailian_web_searchenabled: truedeployments:
<name>:
agent: <string>agent_version: <number> # optionalenvironment: <string> # optionaltunnel: <string> # optional; Qoder BYOC only (see note below)vaults: [ <string> ]memory_stores: [ <string> ]resources: [ DeploymentResource ]initial_events: [ InitialEvent ] # 1..50schedule: { expression: <cron>, timezone: <tz> }description: <string>provider: <string>metadata: { <key>: <string> }initial_events is a discriminated union; schedule.expression must be a 5-field cron expression.
Deployment
tunnelcaveat: Qoder's deployment API does not accepttunnel_id, so the tunnel is dropped from the deployment payload and server-side runs execute without it (validate/planemits a warning). Use sessions for private-network MCP access; see Use BYOC environments.
| Type | Fields |
|---|---|
user.message | content |
system.message | content |
user.define_outcome | description?, rubric? | rubric_file?, max_iterations? (int 1–20) |
| Type | Fields |
|---|---|
file | file_id?, source?, mount_path? |
memory_store | memory_store, access? (read_write|read_only), instructions? |
github_repository | url, checkout? (branch/commit), mount_path?, authorization_token? |