Are you an LLM? Read llms.txt for a summary of the docs, or llms-full.txt for the full context.
Skip to content

Agents

Each directory under agents/ defines a prompt and optional resource configuration. A named agent can root a conversation or run as a delegated specialist.

agents/
├── main/
│   └── AGENT.md
└── compliance-checker/
    ├── agent.json
    └── AGENT.md

Reserved Names (Overrides)

NamePurpose
defaultImplicit conversation root when a chat request omits agent. Its resource declarations apply.
mainOperating instructions for unscoped chat when no authored default exists. Supports agents/main/AGENT.md or agents/main.md.
simpleReserved prompt override, loaded from agents/simple/AGENT.md or agents/simple.md.

Unscoped chat inserts main into the compiled system prompt alongside platform instructions and resource descriptions. It does not replace the whole system prompt. A custom basePrompt in amodal.json overrides the compiled prompt instead.

agents/main/agent.json can opt unscoped chat into file tools through tools. It does not apply the resource restrictions of a named conversation agent. simple is loaded as an override but is not consumed by the current subagent runner; use a named agent with modelTier for a specialist.

Custom Subagents

agent.json + AGENT.md (preferred)

Configuration goes in agent.json. The sibling AGENT.md contains the prompt. If it has a ## Prompt section, that section supplies the prompt; otherwise the whole file does.

{
  "name": "Compliance Checker",
  "description": "Check transactions against the supplied compliance policy.",
  "tools": ["load_knowledge", "store__research_notes__query"],
  "maxToolCalls": 15,
  "modelTier": "advanced"
}
Review the transactions named in the task.
 
1. Load the relevant compliance policy with load_knowledge.
2. Query research notes for the named transactions.
3. Identify violations and cite the policy and transaction evidence.
4. Return confirmed violations, missing evidence, and recommended follow-up.

All configuration fields are optional. An AGENT.md without configuration provides a prompt with default execution settings. A ## Config YAML section in AGENT.md is not parsed; use agent.json or frontmatter.

Rooting a conversation

Pass the directory name in the chat request's agent field:

{
  "message": "Review transactions TXN-001 through TXN-050.",
  "agent": "compliance-checker"
}

When used as a conversation root, its prompt replaces the default operating instructions. Its tools, skills, connections, stores, mcp, and subagents declarations select the available resources. The runtime still supplies chat protocol tools such as stop_execution and ask_choice.

When agent is omitted, chat selects agents/default/ if present and applies that agent's resource scope. Without an authored default, it uses unscoped chat with the bundle's resources. The active root and default are excluded from the callable specialist list.

agent.ts (capabilities that depend on who is asking)

A sibling agent.ts can add synchronous predicates to resource entries. Declare the context fields each predicate reads directly; the definition needs no Amodal package dependency:

const isManager = (ctx: { claims: Record<string, string> }) => ctx.claims['role'] === 'manager';
const isJoint = (ctx: { context: Record<string, string> }) => ctx.context['mission_type'] === 'joint';
 
export default {
  tools: ['review', 'check_conflicts'],
  skills: ['request-review', { name: 'joint-review', conditional: isJoint }],
  subagents: ['prescreen', { name: 'adjudicator', conditional: isManager }],
  stores: {
    requests: 'read',
    decisions: { mode: 'rw', conditional: isManager },
  },
};

List entries accept a name or {name, conditional}. Store entries accept "read", "rw", or {mode, conditional}. agent.json accepts the object forms without conditional.

A predicate can only subtract

A predicate decides whether a declared entry is included for this caller. It cannot add an undeclared resource. It must return true to include the entry; a thrown error or missing caller context excludes it.

ctx.claims vs ctx.context

interface AgentSurfaceContext {
  claims: Record<string, string>;
  context: Record<string, string>;
  scopeId: string;
  userId?: string;
  orgId?: string;
  humanPresent: boolean;
  isSubagent: boolean;
  agentName: string;
}
FieldMeaning
claimsVerified JWT claims. Use for authorization decisions.
contextMerged request context, including client-supplied values. Use to select relevant workflows or resources.
scopeIdThe session's scope identifier; empty for agent-level state.
userId, orgIdVerified caller and organization identifiers, when available.
humanPresentWhether the originating session has a human available to answer.
isSubagentWhether the agent is running as a delegated specialist.
agentNameName of the session's root agent, including in specialist predicates.

A client can send {"role": "manager"} in request context. A permission predicate must therefore read ctx.claims.role, not ctx.context.role.

isSubagent, and when you need it

A delegated specialist inherits the session's surface context with isSubagent set to true. It has no interactive approval handler, even when the inherited humanPresent is true. For contextual tool selection, account for delegation explicitly:

{name: 'lookup', conditional: (ctx) => ctx.isSubagent || ctx.context.page === 'requests'}

This permits the declared tool for a specialist even when the user is on another page. It does not make an inherited tool available if the parent registry lacks it. File tools and a child's native connections are prepared separately from inherited tools.

Rules worth knowing

  • Predicates must be synchronous and pure. Do not fetch data or change state in them.
  • Excluding a skill also excludes the tools contributed by its allowedTools, unless another declaration grants them.
  • agent.ts fields override matching agent.json fields and produce a warning. Keep a field in one file when possible.
  • A conditional in agent.json is a load error.
  • agent.ts requires a default export. Named exports are rejected.
  • AGENT.md is required even when configuration lives entirely in agent.ts.

It re-resolves when the caller changes

Conditional entries are resolved at session creation and checked on later turns. If the resolved resource set changes, the runtime rebuilds it while retaining the conversation. Agents without conditionals skip this check.

CONTEXT.md (facts that change while the user works)

AGENT.md is rendered when the session is built. Values used there stay fixed in that prompt. Put changing facts in a sibling CONTEXT.md, which renders on each turn after the cached prompt prefix.

agents/requests/
├── agent.json
├── AGENT.md
└── CONTEXT.md

Both files use Nunjucks and access request context as scope:

{% if scope.request_status %}
Request status: {{ scope.request_status }}.
{% endif %}

Send the context with each chat request:

{
  "message": "Why has this not been approved?",
  "agent": "requests",
  "context": {
    "mission_id": "m-42",
    "request_status": "Pending review"
  }
}

Which file does a fact go in?

FactSource
Agent role and stable instructionsAGENT.md.
Current selection, status, or page stateCONTEXT.md.
Data that must be verified against a serverA tool call.
Whether the caller may use a capabilityA predicate using verified claims in agent.ts.

A changing value in AGENT.md can produce a stale answer until the prompt is rebuilt. Prompt instructions do not enforce tool permissions.

Notes

  • CONTEXT.md is optional.
  • Values are inert text. Template syntax inside a value is not executed.
  • Values are strings. "false" and "0" are truthy; compare them explicitly when needed.
  • Empty values are dropped, so missing and empty values behave alike.
  • Context has no value-length cap. The runtime logs prompt_template_scope_large above 100,000 total characters; keep per-turn context small.
  • Wrap literal template syntax in {% raw %}...{% endraw %}.

AGENT.md Format (frontmatter)

When there is no agent.json or agent.ts, frontmatter can supply configuration:

---
displayName: Vendor Lookup
description: Look up vendor records and summarize contract status.
tools: [request]
maxToolCalls: 10
modelTier: simple
---
 
Query the vendor system for the named vendor. Return the company name,
contract status, source record, and any missing information.

Configuration Fields

FieldDefaultMeaning
nameDirectory nameDisplay name in agent.json or agent.ts; use displayName in frontmatter. The directory name remains the invocation identifier.
descriptionDisplay nameDescription shown in the specialist list.
tools[]Explicit tool names. For delegation, inherited tools must be in the parent registry; declared file tools can be built separately.
skillsNoneSkills included when the agent roots a session. Each adds its allowedTools.
connectionsNoneREST connections for a root session. Native OpenAPI specialists also prepare their own declared connections.
mcpNoneMCP servers exposed when the agent roots a session.
subagentsNoneSpecialists callable from a session rooted in this agent.
storesNonePer-store "read" or "rw" access for a root session. Shared stores remain read-only.
maxToolCalls10Delegated model-turn limit, despite the field name. One turn may call several tools. Positive integer.
modelTierSession modelDelegation model tier: simple, default, or advanced; see below.
maxDepth1Accepted metadata; the runner uses a fixed five-level delegation limit.
timeout20Accepted metadata in seconds; the delegated runner uses the caller's cancellation signal and runtime tool deadlines.
targetOutputMin, targetOutputMax200, 400Accepted nonnegative metadata; the runner does not enforce these output lengths. State the required format in the prompt.

Model Tiers

The deployment maps simple and advanced to models through platform settings or MODEL_SIMPLE and MODEL_ADVANCED. default uses the session model. An unbound tier falls back to the session model. Inline delegated calls use these bindings; durable subagent execution uses its supplied provider.

Available Tools

See Tools for tool names and registration requirements. For a delegated specialist, name the tools it needs explicitly, including load_knowledge or load_skill for reference material. It does not inherit the parent's compiled prompt, skill bodies, or conversation history.

How Subagents Are Dispatched

The root agent calls call_subagent with one or more named tasks:

call_subagent({
  calls: [
    {
      subagent: 'compliance-checker',
      task: 'Check transactions TXN-001 through TXN-050 against the compliance policy.',
    },
  ],
});

Each invocation accepts at most 12 tasks and runs at most five concurrently. A single task returns its result directly; a batch returns one result or error per specialist.

Subagents run without an interactive approval handler. A call requiring confirmation is denied. A failing tool returns an error for the specialist to handle; a provider failure raises a subagent error.

Context Isolation

A specialist receives its own authored prompt, task, optional structured input, and explicitly forwarded attachments. It returns text to the caller. Include the records and question it needs in the task or input; it cannot read the parent's conversation.

Use a specialist when the task benefits from a separate prompt and tool scope. Use a composite tool to coordinate a fixed sequence of specialists and deterministic steps.