Assembly LineDocs

Declarative Reference

Every agent.md field, default, accepted value, and typed exception.

Edit

This page is the canonical and complete authoring contract for agent.md. Compiler behavior, CLI output, examples, and every other developer guide must conform to this reference. It answers four questions for every field:

  1. What happens when I omit it?
  2. What values compile?
  3. What may I override?
  4. Where does configuration that is not accepted here belong?

Canonical plugin-selection grammar

Every installable feature is delivered by an Agent Plugin. Its location in agent.md declares the role in which the plugin is activated; authors do not repeat that role in a generic plugin envelope.

Role cardinalityDefaultsTyped deviations
Singular (deploy, state, blob, sandbox, secrets, observability, context)Scalar plugin name: state: postgresOne-key mapping: state: { postgres: { connectionEnv: DATABASE_URL } }
Plural (channels, capabilities)List of plugin names: channels: [slack, photon]Name-to-options mapping: channels: { slack: {}, photon: { audio: openai } }

The normalized compiler representation is { name, options } for a singular role and a list of those references for a plural role. These authoring forms are intentionally not accepted:

channels: slack            # plural fields are never scalars
channels: slack, photon    # comma-separated strings are not lists
state: [postgres]          # singular fields are never lists
state:
  use: postgres            # there is no use/with wrapper
  with:
    connectionEnv: DATABASE_URL

The rules are uniform:

  • Omit a field to use its documented default.
  • Use a scalar or list when selected plugins keep their defaults.
  • Use a mapping only when a selected plugin needs a schema-validated deviation.
  • An empty mapping or null entry means “use this plugin's defaults.”
  • Unknown fields, plugin names, and options fail compilation.
  • Routes, packages, credentials, capabilities, and provider metadata stay with the plugin that owns them.

plugins.lock is the dependency and authority lock for every installed Agent Plugin. capabilities: is different: it activates packages that contribute tools, connections, Skills, hooks, or other optional executable capabilities. A plugin selected under channels: or any singular typed field is installed and locked, but is not repeated under capabilities:. Channel selection also activates tools owned by that channel plugin. Optional connections remain explicit capabilities, even when the same package contributes both a channel and a connection. The same typed-selection rule applies to qualified context, observability, composition, and automation-lifecycle references.

Inspect the complete result without copying it back into source:

assembly-line validate agent
assembly-line inspect agent --resolved

Find the change

You need to changeUse
Identity, model, reasoning, or loop limitIdentity and model policy
Deploy target, database, blobs, sandbox, or secretsInfrastructure and Secrets
Ingress, delivery, attachments, or audio transcriptionChannels
Tools, connections, hooks, context, or another executable capabilityCapabilities
A timer or provider event responseAutomations
Runtime-created Skills, automations, or connectionsMutability
Turn-specific instructions, reasoning, model, sandbox, or tool availabilityComposition
Conversation history and transcript limitsContext
Final response shapeStructured output
Telemetry and content captureObservability
DelegationSubagents

Smallest valid agent

model and a non-empty Markdown body are the only required author inputs:

---
model: openrouter/openai/gpt-5.4-mini
---

You are a concise assistant.

Kernel-local deploy, state, blob, and sandbox implementations plus process-env secrets and the default context are selected automatically. Scheduling is kernel orchestration over the chosen state store, not an authoring field or a fake provider. No channels, capabilities, automations, subagents, external telemetry, or structured-output constraint are added. The model prefix still requires its installed and locked model plugin; assembly-line init supplies the OpenRouter plugin.

Set id before production use. Without it, durable state is scoped to the content-derived agent revision and a source change creates a new scope.

agent.md fields

FieldTypeOmitted behavior
modelNon-empty provider/model stringRequired. Compilation fails.
idNon-empty stringDurable scope uses agentRevision.
nameNon-empty stringNo authored display name.
descriptionNon-empty stringNo authored description. Required in every listed subagent.
reasoningoff, minimal, low, medium, high, xhigh, or maxNo effort is authored. Pi uses medium; another harness uses its own default.
maxReasoningSame values as reasoningNo authored ceiling. When set, it must be at least reasoning.
maxIterationsPositive integerRuntime limit is 25 model iterations.
audienceIsolationBooleanfalse.
deployDeploy pluginlocal.
stateState pluginlocal.
blobBlob pluginlocal.
sandboxSandbox pluginlocal, mounted at /workspace.
channelsChannel selectionNo public channel.
timezoneIANA timezone stringUTC.
capabilitiesPublished capability selectionNo published capability plugins. Local plugins activate by presence.
automationsAutomation mappingNone.
mutabilitystatic or policy mappingSkills and automations are autonomous; external connections are disabled.
compositionRule list or plugin contributionThe top-level model, reasoning, instructions, sandbox, and capabilities apply to every turn.
subagentsList of child directory namesNone.
outputSchemaInline JSON Schema or contained .json pathFinal output is unconstrained.
secretsenv, 1password, or 1Password mappingProcess environment.
observabilityotlp, OTLP mapping, or plugin contributionNo external sink; model content capture is off.
contextdefault, default-context mapping, or plugin contributionBuilt-in defaultContext.

There is no top-level metadata, media, tools, hooks, connections, runtime, infrastructure, scheduler, defaultChatModel, privacy, or raceGoal field. Put a setting in the typed field that consumes it. Put executable behavior in a plugin.

The YAML parser accepts YAML 1.2 core values. Duplicate keys, aliases, anchors, merge keys, and explicit tags fail. agent.md is limited to 1 MiB, frontmatter to 256 KiB, 10,000 YAML nodes, and 64 levels.

Identity and model policy

Add only the identity or model policy the product needs:

id: coachgpt
name: CoachGPT
description: Reviews training and recovery data.
model: openrouter/openai/gpt-5.4-mini
reasoning: medium
maxReasoning: high
maxIterations: 18
audienceIsolation: true

model is declared once. Conditional model changes belong under composition; plugins may declare the models their composition contribution can select. defaultChatModel is not an authoring field.

The first path segment is a plugin-owned provider ID. Compilation resolves it from exactly one installed and locked models contribution, obtains static metadata from that plugin, and packages only the selected provider graph. The runtime imports the same package's assemblyLineModelProvider registration into the otherwise provider-neutral Pi loop. An unknown provider needs no compiler, Pi, Node, runtime, or CLI registry edit.

The official model plugins are:

PrefixPackageAuthenticationNotes
openrouter/@assemblyline-agents/openrouterOPENROUTER_API_KEY or generic interactive credential storageDefault scaffold; owns dynamic discovery, video payloads, max reasoning, embeddings, and OpenRouter usage reconciliation.
openai/@assemblyline-agents/openaiOPENAI_API_KEY or generic interactive credential storageOwns OpenAI discovery, embeddings, and usage reconciliation.
openai-codex/@assemblyline-agents/openai-codexPi-native browser or device-code OAuthWraps Pi's standard openaiCodexProvider(); uses ChatGPT subscription billing and the generic durable model-credential store. It does not use the removed custom Codex app-server harness.

Install and select from the plugin declaration:

assembly-line add openrouter agent --role model
assembly-line add openai-codex agent --role model
assembly-line auth openai-codex agent

plugins.lock pins the selected package authority. Subagents that choose the same provider reuse the host's provider credential store; credentials never enter agent.md, the lockfile, or model context.

Infrastructure

A clean hosted selection stays one line per concern:

deploy: hetzner
state: postgres
blob: r2
sandbox: e2b

The built-in runtime is always Node. Provider mappings accept only the options listed below. packageName, configuration/credential declarations, capabilities, generic options, and credential values are resolved provider data, not agent fields.

The framework kernel owns only the local selections and process-env secrets. Scheduling remains kernel orchestration over the chosen state adapter and is controlled operationally, not through agent.md. Every other infrastructure name must resolve from exactly one installed, locked Agent Plugin providers contribution. That package owns the public name, role, runtime kind, package binding, defaults, and JSON Schema for deviations; adding a provider does not require a compiler registry edit.

Deploy

PluginRuntime requirementScalar behavior
localNoneRuns the Node host on the current machine.
dockerDockerBuilds an image. It starts a container only when deploy serve mode is enabled.
flyFLY_API_TOKEN and an app from FLY_APP_NAME or appDeploys one durable Fly Machine.
hetznerA named Hetzner host inventory entryPublishes to the selected host with blue-green containers.
railwayRAILWAY_TOKEN and a linked or selected project/servicePublishes the Node service to Railway.

deploy.docker

OptionTypeDefault
imageNon-empty stringassembly-line:<first 12 build-revision characters>
dockerBinNon-empty stringdocker
serveBooleanfalse
portPositive integer3000 when serve: true
cwdNon-empty stringCurrent working directory
volumeNameNon-empty stringGenerated from the agent and deploy environment
containerNameNon-empty stringGenerated from the agent and deploy environment
deploy:
  docker:
    serve: true
    port: 3100

deploy.fly

OptionTypeDefault
appNon-empty stringFLY_APP_NAME; required if the environment does not set it
regionNon-empty stringFLY_REGION, then iad
flyBinNon-empty stringflyctl
orgNon-empty stringFLY_ORG, otherwise unset
internalPortPositive integer3000
detachBooleanfalse
cwdNon-empty stringCurrent working directory
volumeNameNon-empty stringassembly_line_data
memoryNon-empty Fly memory stringUnset
vmSizeNon-empty Fly VM sizeUnset
autoStopBooleanfalse
minMachinesRunningPositive integer1; 0 when autoStop: true
deploy:
  fly:
    app: coachgpt
    region: ord
    memory: 2gb

deploy.railway

OptionTypeDefault
projectNon-empty stringRAILWAY_PROJECT_ID, otherwise the linked project
serviceNon-empty stringRAILWAY_SERVICE_ID, otherwise the linked or only application service
railwayBinNon-empty stringrailway
jsonBooleanfalse
detachBooleanfalse
cwdNon-empty stringCurrent working directory

deploy.hetzner

deploy: hetzner names the plugin. Select the host with deploy.hetzner.host or ASSEMBLY_LINE_HETZNER_HOST.

The inventory, not agent.md, owns the address, SSH user and key, pinned host key, provider resource ID, region, and ingress namespace. The default inventory file is assembly-line.hosts.json; override it with deploy.hetzner.hostsFile or ASSEMBLY_LINE_HETZNER_HOSTS_FILE.

OptionTypeDefault
hostNon-empty inventory nameASSEMBLY_LINE_HETZNER_HOST; required before deploy
environmentNon-empty environment namedevelopment
hostsFileNon-empty pathNearest assembly-line.hosts.json
expectedRegionNon-empty stringNo extra region assertion
ingress.visibilitypublic or privateInventory policy
resources.cpusPositive number1
resources.memoryDocker memory string, such as 2g1g
resources.pidsPositive integer256
database.modeexternal or hostexternal
monitoring.enabledBooleantrue
monitoring.diskFreeMinimumMbPositive integer5120
caddyImageExplicit non-latest image tagcaddy:2.10.0-alpine
postgresImageExplicit non-latest image tagpostgres:17.10-alpine
awsCliImageExplicit non-latest image tagamazon/aws-cli:2.17.57
deploy:
  hetzner:
    environment: production
    ingress:
      visibility: private
    resources:
      cpus: 2
      memory: 2g
    database:
      mode: host

@assemblyline-agents/vps is the plugin package for the hetzner deploy selection. vps is not a declarative selection name.

State

PluginConnectionProvider defaults
localNoneLocal state; no options.
postgresDATABASE_URLTLS enabled; certificate verification enabled.
neonDATABASE_URLSame Postgres contract, tagged as Neon.
supabaseDATABASE_URLSame Postgres contract, tagged as Supabase.
railwayRailway database referencedatabaseService: Postgres, provision: true, sslRejectUnauthorized: false.

All non-local profiles compile to the Postgres runtime. Use state: postgres for an ordinary database URL. Neon, Supabase, and Railway are placement presets, not separate state implementations.

OptionProfilesTypeDefault
connectionEnvAll non-localNon-empty environment variable nameDATABASE_URL
sslAll non-localBooleantrue, unless the URL contains sslmode=disable
sslRejectUnauthorizedAll non-localBooleantrue; Railway uses false
optionalMigrationsAll non-localBoolean or list of migration IDsfalse
databaseServiceRailway onlyNon-empty stringPostgres
provisionRailway onlyBooleantrue
state:
  postgres:
    connectionEnv: COACHGPT_DATABASE_URL
    sslRejectUnauthorized: true

Blob

Blob plugins currently accept no authored options. Bucket names, endpoints, credentials, prefixes, and public URLs come from environment configuration.

PluginRequired environmentDefault
localNoneLocal blob storage.
r2R2_ACCOUNT_ID, R2_ACCESS_KEY_ID, R2_SECRET_ACCESS_KEY, R2_BUCKETR2 S3-compatible endpoint; empty prefix; no public URL.
s3S3_BUCKET, S3_REGION, S3_ACCESS_KEY_ID, S3_SECRET_ACCESS_KEYAWS S3 endpoint; empty prefix; no public URL.

Sandbox

PluginRequired environmentDefault imageNetwork default
localNoneHost processHost network
dockerDockernode:22-slimnone
daytonaDAYTONA_API_KEYnode:22-slimDaytona default
e2bE2B_API_KEYE2B default templateInternet disabled
modalMODAL_TOKEN_ID, MODAL_TOKEN_SECRETnode:22-slimModal default

Every plugin uses /workspace as its working directory unless a named sandbox YAML definition changes it.

sandbox.docker

OptionTypeDefault
networkNon-empty stringnone
cpusNon-empty Docker CPU valueUnset
memoryNon-empty Docker memory valueUnset
pullPolicyalways, missing, or nevermissing
commandTimeoutMsPositive integer30000

sandbox.e2b

OptionTypeDefault
internetenabled, disabled, true, or falsedisabled
templateNon-empty E2B template nameE2B_TEMPLATE, otherwise E2B default
timeoutMsPositive integerASSEMBLY_LINE_E2B_TIMEOUT_MS, otherwise E2B SDK default
retainTimeoutMsPositive integerASSEMBLY_LINE_E2B_RETAIN_TIMEOUT_MS, otherwise unset
requestTimeoutMsPositive integerASSEMBLY_LINE_E2B_REQUEST_TIMEOUT_MS, otherwise SDK default
pauseKeepMemoryBooleantrue

Enable internet only when the agent needs outbound access:

sandbox:
  e2b:
    internet: enabled

sandbox.daytona

OptionTypeDefault
networkAllowListNon-empty Daytona list stringUnset
domainAllowListNon-empty Daytona list stringUnset
ephemeralBooleanfalse
networkBlockAllBooleanDaytona default
autoStopIntervalPositive integer, minutesDaytona default
autoArchiveIntervalPositive integer, minutesDaytona default
autoDeleteIntervalPositive integer, minutesDaytona default
createTimeoutSecondsPositive integerDaytona SDK default
lifecycleTimeoutSecondsPositive integerDaytona SDK default

“Daytona default” means Assembly Line does not send that option. Daytona owns the behavior. The resolved inspector shows that the option is unset.

sandbox.modal

OptionTypeDefault
timeoutMsPositive integerASSEMBLY_LINE_MODAL_TIMEOUT_MS, otherwise Modal SDK default
waitReadyBooleanfalse

Scheduler

ValueBehavior
localThe current Node process polls due work.
postgresPostgres coordinates due work across processes.
gatewayThe deploy gateway triggers scheduled work; local polling is disabled.

Scheduler plugins currently accept no authored options.

Channels

One channel still uses the plural list form:

channels: [photon]

Multiple channels may use a list when no exceptions are needed:

channels: [photon, slack]

Use a mapping for exceptions. If exactly one channel is selected, it becomes the default outbound channel. With multiple channels, no outbound default is inferred; set default: true on at most one.

channels:
  photon: {}
  slack:
    default: true

Official channel plugins

ChannelRoutesRequired environmentDefault audio
a2aPOST /a2a; GET /.well-known/agent-card.jsonA2A_PEER_TOKENS, A2A_PUBLIC_URLDisabled
discordPOST /discord/eventsDISCORD_PUBLIC_KEY, DISCORD_APPLICATION_ID, DISCORD_BOT_TOKENDisabled
photonGET, POST /photon/eventsOne Photon ingress secret group plus delivery credentialsOpenRouter
slackPOST /slack/eventsSLACK_SIGNING_SECRET, SLACK_BOT_TOKENOpenRouter
teamsPOST /teams/messagesMICROSOFT_APP_ID, MICROSOFT_APP_PASSWORDOpenRouter
telegramPOST /telegram/eventsTELEGRAM_BOT_TOKEN; TELEGRAM_WEBHOOK_SECRET for production ingressOpenRouter

The channel owns routes, methods, transport, authentication, normalization, delivery, attachments, scopes, required environment, connection binding, description, and metadata. None of those fields can be restated in agent.md. These declarations live with the channel's plugin.json and ai.assemblyline entry; the compiler has no second official-channel registry.

Common channel options

OptionTypeDefault
defaultBooleanInferred only for a sole channel
audioopenrouter, openai, disabled, false, or one-key provider mappingChannel default in the table above
hooksChannel export name to qualified plugin channel-hook contributionNone

Audio options

Audio is channel functionality. There is no top-level media or audio field. Change a channel's provider, disable transcription, or configure that channel's provider:

channels:
  photon:
    audio: openai
  slack:
    audio: disabled
  telegram:
    audio:
      openrouter:
        model: openai/whisper-large-v3
        language: en
OptionTypeOpenRouter defaultOpenAI default
modelNon-empty stringopenai/whisper-large-v3-turbogpt-4o-mini-transcribe
fallbackModelsList of non-empty stringsopenai/whisper-large-v3, openai/whisper-1whisper-1
languageNon-empty stringUnsetUnset
endpointNon-empty URL stringhttps://openrouter.ai/api/v1/audio/transcriptionshttps://api.openai.com/v1/audio/transcriptions
modelMetadataKeyNon-empty stringUnsetUnset
maxBytesPositive integer2621440026214400
maxAttachmentsPositive integer44
timeoutMsPositive integer6000060000
maxResponseBytesPositive integer524288524288
titleNon-empty stringAssembly Line audio transcriptionNot accepted
refererNon-empty stringPublic app URL, then https://assembly-line.localNot accepted

OpenRouter requires OPENROUTER_API_KEY. OpenAI requires OPENAI_API_KEY. Provider-specific audio environment variables can override omitted values; see the environment reference.

Telegram options

OptionTypeDefault
parseModeMarkdownV2 or HTMLPlain text unless TELEGRAM_PARSE_MODE is set
disableWebPagePreviewBooleanfalse
replyToMessageBooleantrue
allowedUpdatesList of non-empty stringsProvider webhook default

Teams options

OptionTypeDefault
allowedTenantsList of tenant IDsEmpty, which allows any verified tenant
allowedServiceUrlsList of HTTPS service URLsEmpty, which allows verified Bot Framework URLs
stripMentionsBooleantrue
typingBooleantrue

Discord options

OptionTypeDefault
gatewayBoolean or mapping with enabled, intents, urlDisabled unless DISCORD_GATEWAY_ENABLED enables it
gateway.enabledBooleanfalse
gateway.intentsNon-negative integerGuilds, guild messages, direct messages, and message content
gateway.urlNon-empty stringDiscord /gateway/bot discovery
allowedGuildsList of guild IDsEmpty, which allows all guilds
dmSupportBooleantrue
requireMentionBooleantrue for gateway guild messages
allowedMentionsJSON mapping{ parse: [] }

A2A options

channels: [a2a] derives a complete Agent Card:

OptionTypeDefault
nameNon-empty stringAgent name, then id, then Assembly Line Agent
descriptionNon-empty stringAgent description, then Send a task to <name>.
versionNon-empty string1.0.0
skillsNon-empty list of skill mappingsOne message skill derived from the description
provider.organizationNon-empty stringUnset
provider.urlNon-empty stringUnset
documentationUrlNon-empty stringUnset
iconUrlNon-empty stringUnset

Each A2A skill requires id, name, and description. Optional fields are tags, examples, inputModes, and outputModes, each a string list. Skill IDs must be unique.

Photon and Slack accept no plugin-owned options beyond the common channel options.

Capabilities

Checked-in local plugins under plugins/<name>/ activate by presence. Do not list them in agent.md. Published plugins must be installed, selected, and pinned in plugins.lock.

capabilities: [notion]
capabilities: [notion, github]

Mapping form accepts plugin-owned config plus framework-owned, contribution-scoped exceptions:

capabilities:
  meal-memory:
    config:
      dataset: household
  notion:
    tools:
      disable: [delete_page]
      approval:
        update_page: always
    connections:
      notion:
        required: true
        subject: workspace
        disable: [delete_database]
        approval:
          create_page: once
        events:
          include: [page.content_updated]

config is available only when the selected plugin declares a root configSchema in its locked ai.assemblyline entry. The compiler validates the value before generating code. Unknown keys, wrong types, missing required values, invalid schemas, and config on a plugin without a schema fail the build. A list selection validates as {}, so a schema may require explicit configuration.

Configuration is plugin-wide because the package is the cohesive capability boundary. Native tool and connection factories receive package-owned contribution defaults followed by this agent-owned config; hook and lifecycle factories receive the config directly; a composition handler receives it as its argument. Static default exports, Skills, and portable MCP declarations are not rewritten by config. Use a native factory when executable construction depends on per-agent configuration.

config is compiled into the manifest for inspection and must never contain credentials. Plugins classify names explicitly with requiredConfig, optionalConfig, requiredCredentials, and optionalCredentials; deployment and the selected secrets provider supply values without creating a shared runtime secret environment.

Capability plugin configuration

FieldTypeDefault
configJSON mapping accepted by the plugin's locked configSchema{}
toolsTool contribution exceptionsPlugin declarations
connectionsConnection contribution exceptionsPlugin declarations

Tool contribution exceptions

FieldTypeDefault
disableList of contributed tool names or *Plugin-reviewed availability
approvalTool-name mapping to always or neverTool descriptor and host policy

The shared parser accepts once, but native tool compilation rejects it. once is connection-only. Unknown contributed tool names fail.

Connection contribution exceptions

Each key under connections must name a real connection contribution.

FieldTypeDefault
disabledBooleanfalse
disableList of provider action namesPlugin-reviewed action surface
approvalAction-name mapping to always, once, or neverPlugin-reviewed approval policy
subjectuser, workspace, installation, or environmentPlugin declaration
requiredBooleanPlugin declaration
eventsfalse or mapping belowPlugin event defaults
installationenvironmentUnset; accepted only by github-app
installationIdEnvUppercase environment variable nameGITHUB_APP_INSTALLATION_ID; accepted only by github-app

events accepts include and exclude string lists plus a resources list of mappings. The owning connection plugin validates resource shapes. Event names are checked against the plugin catalog.

Plugin contribution selectors

ConcernSyntax
Compositioncomposition: plugin.contribution
Contextcontext: plugin.contribution
Observabilityobservability: plugin.contribution
Automation lifecycleautomations.<name>.lifecycle: plugin.contribution
ChannelA unique contribution name selected directly under channels
Channel hookchannels.<name>.hooks.<export>: plugin.contribution

Plugin Skills and hooks activate with the plugin. They are not selected again in frontmatter.

Automations

automations is a mapping keyed by a name that uses letters, numbers, underscores, or hyphens. Each entry must declare exactly one schedule or trigger.

FieldTypeDefault or rule
descriptionNon-empty stringUnset
scheduleSupported schedule string or five-field cron mappingMutually exclusive with trigger
triggersource.event stringMutually exclusive with schedule
connectionSelected connection contribution nameInferred when the trigger source matches a selected plugin
filterJSON-compatible mappingMatch all; present mappings use recursive JSON-subset matching
messageNon-empty stringRuntime target default
skillRoot Skill nameNo target override
agentSubagent nameNo target override
playbookPlaybook nameNo target override
deliveryNon-empty route or delivery nameNormal default-channel delivery
lifecycleQualified plugin lifecycle contributionNone
enabledBooleantrue
idempotencyNon-empty stringautomation:<name>
timezoneIANA timezoneAgent timezone

At most one of skill, agent, and playbook may be set. There is no automation metadata field.

timezone: America/Chicago
automations:
  morning:
    schedule: weekdays at 09:00
    message: Prepare the morning brief.
  training-review:
    trigger: mirror.source.changed
    filter:
      provider: strava
    skill: review-training
    delivery: silent

Supported schedule strings are:

daily at HH:MM
sunday|monday|tuesday|wednesday|thursday|friday|saturday at HH:MM
weekdays at HH:MM
every N minutes              # N is 1 through 59
monthly on day N at HH:MM    # N is 1 through 31

Use a five-field cron for any other schedule:

schedule:
  cron: "0 9 1 * *"
  timezone: America/Chicago

Move a large automation map into one contained YAML file:

automations:
  include: ./automations.yaml

The include must be a contained .yaml or .yml mapping. Inline and included names must be unique.

Mutability

Mutability controls what the running agent may add to durable state. It does not control source edits, plugin authority, sandbox filesystem access, or provider permissions.

SurfaceOmitted valueAccepted values
skillsautonomousautonomous, approval, disabled
automationsautonomousautonomous, approval, disabled
externalAccessdisableddisabled or an enabled mapping

Omitted mapping keys keep the same defaults. Change only the exception:

mutability:
  automations: approval

mutability: static disables all three surfaces. Enabled external access requires a mode and a non-empty host allowlist:

mutability:
  externalAccess:
    mode: approval
    allowedHosts: [api.example.com, "*.example.org"]

mode is autonomous, approval, or disabled. A host matches itself and its subdomains; a leading *. is optional and normalized away.

Composition

Composition applies typed turn-time exceptions in order. Each rule contains one when condition or otherwise: true, plus at least one action.

maxReasoning: high
composition:
  - when:
      channel: slack
    reasoning: high
    instructions: Reply for a workplace audience.
    capabilities:
      disable: [delete_page]
  - when:
      principal.roles:
        includes: admin
    capabilities:
      enable: [delete_page]
  - otherwise: true
    reasoning: low
ConditionCompared value
channelString
principal.typeString
principal.idString
principal.roles{ includes: <role> }
audience.privateBoolean
attachments.presentBoolean
state.<key>String or Boolean
ActionType
modelNon-empty provider/model string
reasoningSupported reasoning level, no higher than maxReasoning
instructionsNon-empty appended instruction string
capabilities.enableList of existing tool, Skill, connection, or subagent names
capabilities.disableList of existing tool, Skill, connection, or subagent names

Only one otherwise rule is allowed. Unknown condition fields, actions, state keys with empty names, and capability names fail compilation. Use a qualified plugin contribution when the policy needs executable logic:

composition: company.access-policy

Context

Omit context or use context: default for defaultContext. Only the default profile accepts an author mapping:

context:
  default:
    recentHistory:
      maxMessages: 12
    transcript:
      resume: true
      reserveTokens: 16384
      keepRecentTokens: 20000
      toolResultCapChars: 2000
OptionTypeDefault
recentHistory.maxMessagesNon-negative integerNo extra cap; normal conversation lookup supplies up to 20 stored messages
transcript.resumeBooleantrue
transcript.reserveTokensPositive integer16384
transcript.keepRecentTokensPositive integer20000
transcript.toolResultCapCharsPositive integer2000

Use a mapping only when the agent needs a fixed override. Custom executable context behavior requires a selected contribution:

context: company.private

Structured output

Use an inline JSON Schema:

outputSchema:
  type: object
  additionalProperties: false
  required: [answer]
  properties:
    answer:
      type: string

Or use a contained JSON file:

outputSchema: schemas/answer.json

Absolute paths, traversal, missing files, non-JSON files, and invalid JSON Schema fail compilation.

Secrets

Omission and secrets: env select the framework-kernel host-environment secret store. Values are still resolved only for scoped credential consumers; they are not projected into a shared runtime environment. secrets: 1password selects the installed and locked official 1Password plugin. Its mapping accepts only:

OptionTypeDefault
vaultNon-empty stringOP_VAULT; required if the environment does not set it
fieldNon-empty stringcredential
secrets:
  1password:
    vault: Production Runtime
    field: credential

Secret values never belong in agent.md. See Configuration And Credentials for broker lifecycle, sandbox leases, and secure source-to-sink transfer.

Observability

Omission configures no external sink. The runtime still keeps its durable run records. Install and lock the official plugin with assembly-line add otlp agent, then observability: otlp selects its package-owned instrumentation factory and requires OTEL_EXPORTER_OTLP_ENDPOINT.

OptionTypeDefault
serviceNameNon-empty stringAgent name
captureContentoff, usage, content, full, or policy mappingusage, which records usage but no model content
recordInputsBooleanfalse
recordOutputsBooleanfalse

The captureContent mapping requires level and accepts these exceptions:

OptionTypeDefault
leveloff, usage, content, or fullRequired
maxCharsPositive integer8000; 200000 for full
redactBooleantrue
redactKeysList of non-empty stringsEmpty
sampleRateNumber from 0 through 11
includeToolIOBooleantrue for content and full; otherwise false
observability:
  otlp:
    serviceName: coachgpt
    captureContent:
      level: usage
      redact: true
      sampleRate: 0.25
    recordInputs: false
    recordOutputs: false

Use an installed and locked qualified plugin contribution for a custom sink:

observability: company.telemetry

Subagents

List child directory names in the parent:

subagents: [researcher, reviewer]

Each name must use letters, numbers, underscores, or hyphens and must contain subagents/<name>/agent.md. A child is a complete MD-first surface with its own required model, non-empty instructions, and required description. Children may declare their own channels, capabilities, Skills, automations, sandbox, context, composition, and output schema. The deployed root still owns the physical runtime, state adapter, and blob adapter.

Named sandbox YAML

Use sandbox/<name>.yaml when a provider scalar is not enough. The filename is the profile name; do not repeat name inside the file.

# sandbox/coding.yaml
adapter: e2b
image: node:22
workingDirectory: /workspace
env: [GITHUB_TOKEN]
snapshot:
  mode: on_failure
  retainLast: 2
environment:
  context: coding-environment
  dockerfile: Dockerfile
  verifyCommand: node --version

Select it with:

sandbox: coding
FieldTypeDefault
adapterNon-empty provider nameRequired
imageNon-empty image or provider image referenceProvider default
workingDirectoryNon-empty path/workspace
envList of environment variable names to projectEmpty
snapshot.modenever, manual, on_failure, or alwaysNo snapshot policy
snapshot.retainLastNon-negative integerRuntime snapshot retention default
snapshot.reasonNon-empty stringUnset
snapshot.metadataJSON mappingEmpty
environment.contextContained relative directoryRequired when environment is present
environment.dockerfileFile inside contextDockerfile
environment.verifyCommandNon-empty stringUnset

The compiler contains and hashes every environment file. Symlinks, traversal, YAML aliases, anchors, and explicit tags fail. A named sandbox YAML file is the only declarative surface where snapshot-owned metadata is accepted.

Other agent files

SurfaceOmitted behaviorAdd it when
Markdown bodyInvalid when emptyAlways; permanent trusted system instructions
skills/*/SKILL.mdNo root SkillsThe model needs optional procedures or reference material
plugins/<name>/plugin.jsonNo local pluginThe agent needs custom executable capability code
plugins.lockPublished plugins cannot resolveCLI workflows install and pin a published or local plugin
automations.yamlInline automations onlyautomations.include points to this contained map
subagents/<name>/agent.mdNo childThe parent lists the child under subagents
evals/No evaluation casesThe agent needs regression tests; eval files do not affect deployment revision
assets/No extra resourcesInstructions or plugins need stable source assets
migrations/No agent-owned migrationsA state provider needs additional deployment migrations

Root agent.ts, gateway.ts, context.ts, instrumentation.ts, tools/, hooks/, connections/, channels/, automations/, schedules/, lib/, and other executable conventions are rejected. Put executable behavior in a local or published plugin.

On this page