Agents
Agent Reference
This is the authoritative reference for the agent format. An agent consists of two files: AGENT.md (persona) and agent.json (operational config).
AGENT.md
AGENT.md is pure prose markdown. No frontmatter is required. Its entire content replaces the agent's default identity in the system prompt when the agent is active.
Guidelines
- Write in second person ("You are a...")
- Define the agent's job, communication style, and boundaries
- Be specific about what the agent should and should not do
- Structure with headings for readability
- There is no schema -- write whatever makes the persona clear
agent.json Schema
{
"skills": ["@org/skills/name", "@org/skills/name@version"],
"defaults": {
"timezone": "user_local",
"configurable": ["workflows.morning-briefing.trigger.cron"]
},
"pricing": {
"model": "monthly_fixed",
"cost": 47.0
},
"workflows": {
"binding-name": {
"trigger": {},
"description": "string",
"inputs": {},
"activities": [],
"budget": {},
"emit": "string"
}
}
}
Top-Level Fields
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
skills |
string[] | No | [] |
Qualified skill references this agent depends on. Auto-installed when the agent is installed. |
defaults |
object | No | {} |
Default settings applied to the agent. |
defaults.timezone |
string | No | "user_local" |
Timezone used for schedule triggers. |
defaults.configurable |
string[] | No | [] |
Dot-path list of fields the owner may override at install time. |
pricing |
object | No | {} |
Marketplace pricing configuration. |
pricing.model |
string | No | -- | Pricing model (e.g. "monthly_fixed", "per_run"). |
pricing.cost |
number | No | -- | Dollar amount charged per pricing period or run. |
workflows |
map | No | {} |
Map of binding name to WorkflowBinding. Keys are the binding names used in logs and debugging. |
Skill References
Skills are referenced by qualified name, not plain name:
@org/skills/name-- latest version@org/skills/name@version-- pinned version
At install time, skills can also be installed via code: SKIL-XXXX-XXXX.
WorkflowBinding
Each value in the workflows map is a binding that defines what to run and when. The key is the binding name.
| Field | Type | Required | Description |
|---|---|---|---|
trigger |
Trigger | Yes | When this binding fires. |
description |
string | Yes | Human-readable description of what this binding does. |
inputs |
object | No | Arbitrary key-value inputs passed to the binding at runtime. |
activities |
AgentActivity[] | Yes | Inline activities executed in order. |
budget |
AgentBudget | Yes | Cost budget for the entire binding run. |
emit |
string | No | Named event emitted on successful completion of all activities. |
AgentBudget
| Field | Type | Required | Description |
|---|---|---|---|
total_per_run |
integer | Yes | Maximum token spend per single run of this binding. |
cost_estimate |
string | Yes | Human-readable cost estimate (e.g. "~$0.03"). |
Trigger Types
Schedule
Fires on a schedule defined by a standard cron expression (5-field).
{
"type": "schedule",
"cron": "0 8 * * 1-5"
}
| Field | Type | Required | Description |
|---|---|---|---|
type |
"schedule" |
Yes | -- |
cron |
string | Yes | 5-field cron expression. |
Cron field reference: minute hour day-of-month month day-of-week
| Expression | Meaning |
|---|---|
0 8 * * 1-5 |
Weekdays at 08:00 |
*/15 * * * * |
Every 15 minutes |
0 0 1 * * |
First day of every month at midnight |
0 9,17 * * * |
09:00 and 17:00 daily |
Heartbeat
Fires at a fixed interval. The first execution happens one interval after the agent activates. An optional window restricts execution to certain hours.
{
"type": "heartbeat",
"interval": "30m",
"window": "08:00-18:00"
}
| Field | Type | Required | Description |
|---|---|---|---|
type |
"heartbeat" |
Yes | -- |
interval |
string | Yes | Duration string: "30s", "5m", "1h", "2h30m". |
window |
string | No | Time-of-day window restricting when the heartbeat fires (e.g. "08:00-18:00"). |
Event
Fires when one or more named events are emitted by another binding's emit field.
{
"type": "event",
"sources": ["calendar.changed", "email.urgent"]
}
| Field | Type | Required | Description |
|---|---|---|---|
type |
"event" |
Yes | -- |
sources |
string[] | Yes | List of event names to subscribe to. Each must match an emit field in another binding. |
Event namespacing: At runtime, events are namespaced by the agent's slug. For example, if the agent slug is chief-of-staff, an emitted event briefing.ready becomes chief-of-staff.briefing.ready.
Manual
Only fires when explicitly invoked by the user or another system component.
{
"type": "manual"
}
| Field | Type | Required | Description |
|---|---|---|---|
type |
"manual" |
Yes | -- |
AgentActivity
Each activity is a discrete unit of work within a binding.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
id |
string | Yes | -- | Unique identifier within the binding. |
intent |
string | Yes | -- | Natural language description of the goal. This is the prompt given to the LLM. |
skills |
string[] | No | [] |
Skills loaded into context for this activity. Must be a subset of the agent's top-level skills. |
mcps |
string[] | No | [] |
MCP server names made available to this activity. |
cmds |
string[] | No | [] |
System commands the activity is allowed to execute. |
model |
string | Yes | -- | LLM model for this activity. |
steps |
string[] | No | [] |
Ordered sub-steps for decomposing complex activities. |
token_budget |
object | Yes | -- | Token budget for this activity. |
token_budget.max |
integer | Yes | -- | Maximum tokens (input + output) allocated for this activity. |
on_error |
object | Yes | -- | Error handling policy for this activity. |
on_error.retry |
integer | Yes | -- | Number of retries before falling back. |
on_error.fallback |
string | Yes | -- | Fallback action: "NotifyOwner", "Skip", or "Abort". |
Error Handling
| Fallback | Behavior |
|---|---|
"NotifyOwner" |
Notify the owner of the error after retries are exhausted. |
"Skip" |
Log the error and continue to the next activity. The binding can still emit on completion. |
"Abort" |
Halt the entire binding. No further activities execute. The emit event is not fired. |
Token Budgets
Token budgets prevent runaway costs. The budget applies to the total tokens (input + output) consumed by the activity's LLM calls.
- If an activity exceeds its budget, execution stops for that activity.
- The
on_errorpolicy determines whether the binding continues. - Set conservative budgets for simple tasks (notifications: 512) and larger budgets for research work (4096+).
Emit Chains
The emit field on a binding fires a named event after all activities complete successfully. Other bindings with matching event triggers will fire in response.
Chain Example
{
"workflows": {
"collect-data": {
"trigger": { "type": "schedule", "cron": "0 */6 * * *" },
"description": "Scrape latest data from configured sources",
"inputs": {},
"activities": [
{
"id": "scrape",
"intent": "Scrape latest data from configured sources",
"skills": ["@nebo/skills/web-scraper"],
"mcps": [],
"cmds": [],
"model": "claude-sonnet",
"steps": [],
"token_budget": { "max": 4096 },
"on_error": { "retry": 1, "fallback": "Abort" }
}
],
"budget": { "total_per_run": 5000, "cost_estimate": "~$0.04" },
"emit": "data-collected"
},
"analyze-data": {
"trigger": { "type": "event", "sources": ["data-collected"] },
"description": "Analyze collected data for trends and anomalies",
"inputs": {},
"activities": [
{
"id": "analyze",
"intent": "Analyze collected data for trends and anomalies",
"skills": ["@nebo/skills/data-analyzer"],
"mcps": [],
"cmds": [],
"model": "claude-sonnet",
"steps": [],
"token_budget": { "max": 4096 },
"on_error": { "retry": 1, "fallback": "Abort" }
}
],
"budget": { "total_per_run": 5000, "cost_estimate": "~$0.04" },
"emit": "analysis-complete"
},
"report": {
"trigger": { "type": "event", "sources": ["analysis-complete"] },
"description": "Generate a summary report and notify the user",
"inputs": {},
"activities": [
{
"id": "generate-report",
"intent": "Generate a summary report and notify the user",
"skills": ["@nebo/skills/report-writer"],
"mcps": ["slack-notifier"],
"cmds": [],
"model": "claude-sonnet",
"steps": [],
"token_budget": { "max": 2048 },
"on_error": { "retry": 1, "fallback": "Skip" }
}
],
"budget": { "total_per_run": 3000, "cost_estimate": "~$0.02" }
}
}
}
This creates a three-stage pipeline: schedule -> collect -> analyze -> report, where each stage is independently testable and has its own error handling.
Cycle Prevention
Emit chains must be acyclic. If binding A emits event X and binding B subscribes to X and emits Y, binding A must not subscribe to Y. Cycles are detected at agent load time and rejected with an error.
Installation Lifecycle
- Install: User enters
AGNT-XXXX-XXXXcode or installs from the marketplace. - Cascade: All skills in the agent's
skillsarray are auto-installed. If those skills have their owndependencies, those are installed too. - Activate: The AgentWorker starts.
AGENT.mdis injected into the system prompt. - Runtime: The AgentWorker parses
agent.json, starts heartbeat loops, registers cron schedules, and subscribes to events. - Execution: When a trigger fires, activities execute in order. On completion,
emitevents propagate to downstream bindings. - Deactivate: Switching agents or uninstalling stops the AgentWorker, cancels all schedules and subscriptions.
AgentWorker Runtime
The AgentWorker is a per-agent runtime that manages the lifecycle of all bindings:
| Responsibility | Detail |
|---|---|
| Schedule scheduling | Registers cron expressions with the system scheduler. |
| Heartbeat loops | Spawns a timer loop for each heartbeat binding. Respects window constraints. |
| Event subscriptions | Subscribes to named events from the internal event bus. |
| Activity execution | Executes activities sequentially within a binding. Loads skills, connects MCPs, enforces token budgets. |
| Error handling | Applies on_error policy per activity. Retries, then falls back. Logs all errors. |
| Emit propagation | Fires named events on binding completion. Events are namespaced by agent slug. |
| Lifecycle management | Starts on agent activation, stops on deactivation. Cleans up all timers and subscriptions. |
Install Codes
AGNT-XXXX-XXXX
- Prefix:
AGNT- - Encoding: Crockford Base32 (excludes I, L, O, U for readability)
- Two groups of 4 characters separated by a hyphen
Complete agent.json Example: Chief of Staff
{
"skills": [
"@nebo/skills/quick-research",
"@nebo/skills/web-scraper",
"@nebo/skills/note-taker",
"@nebo/skills/report-writer"
],
"defaults": {
"timezone": "user_local",
"configurable": [
"workflows.morning-briefing.trigger.cron"
]
},
"pricing": {
"model": "monthly_fixed",
"cost": 47.0
},
"workflows": {
"morning-briefing": {
"trigger": {
"type": "schedule",
"cron": "0 7 * * 1-5"
},
"description": "Gather overnight updates and deliver a morning briefing",
"inputs": {
"sources": ["calendar", "email", "slack"]
},
"activities": [
{
"id": "gather-updates",
"intent": "Collect overnight calendar changes, unread emails, and Slack highlights",
"skills": ["@nebo/skills/quick-research", "@nebo/skills/web-scraper"],
"mcps": ["google-calendar", "gmail"],
"cmds": [],
"model": "claude-sonnet",
"steps": [
"Fetch calendar events for today",
"Scan unread emails for action items",
"Pull Slack highlights from key channels"
],
"token_budget": { "max": 4096 },
"on_error": { "retry": 1, "fallback": "NotifyOwner" }
},
{
"id": "write-briefing",
"intent": "Synthesize gathered updates into a structured morning briefing and save it",
"skills": ["@nebo/skills/note-taker", "@nebo/skills/report-writer"],
"mcps": [],
"cmds": [],
"model": "claude-sonnet",
"steps": [
"Summarize top priorities",
"List schedule conflicts",
"Draft briefing note"
],
"token_budget": { "max": 2048 },
"on_error": { "retry": 1, "fallback": "Abort" }
}
],
"budget": {
"total_per_run": 7000,
"cost_estimate": "~$0.05"
},
"emit": "briefing.ready"
},
"notify-owner": {
"trigger": {
"type": "event",
"sources": ["briefing.ready"]
},
"description": "Send a Slack DM when the briefing is ready",
"inputs": {},
"activities": [
{
"id": "send-slack",
"intent": "Send a Slack DM notifying the owner their morning briefing is ready",
"skills": [],
"mcps": ["slack"],
"cmds": [],
"model": "claude-haiku",
"steps": [],
"token_budget": { "max": 512 },
"on_error": { "retry": 2, "fallback": "Skip" }
}
],
"budget": {
"total_per_run": 1000,
"cost_estimate": "~$0.01"
}
},
"health-check": {
"trigger": {
"type": "heartbeat",
"interval": "30m",
"window": "08:00-18:00"
},
"description": "Verify that all configured data sources are reachable",
"inputs": {},
"activities": [
{
"id": "check-sources",
"intent": "Verify that all configured data sources are reachable and responding",
"skills": ["@nebo/skills/web-scraper"],
"mcps": [],
"cmds": [],
"model": "claude-haiku",
"steps": [],
"token_budget": { "max": 256 },
"on_error": { "retry": 2, "fallback": "NotifyOwner" }
}
],
"budget": {
"total_per_run": 500,
"cost_estimate": "~$0.003"
}
},
"deep-dive": {
"trigger": {
"type": "manual"
},
"description": "On-demand deep-dive research session on a user-specified topic",
"inputs": {},
"activities": [
{
"id": "research",
"intent": "Perform a deep-dive research session on the user's specified topic",
"skills": ["@nebo/skills/quick-research", "@nebo/skills/web-scraper", "@nebo/skills/note-taker"],
"mcps": [],
"cmds": [],
"model": "claude-sonnet",
"steps": [],
"token_budget": { "max": 8192 },
"on_error": { "retry": 1, "fallback": "Abort" }
},
{
"id": "report",
"intent": "Generate a comprehensive report from the deep-dive findings",
"skills": ["@nebo/skills/report-writer"],
"mcps": [],
"cmds": [],
"model": "claude-sonnet",
"steps": [],
"token_budget": { "max": 4096 },
"on_error": { "retry": 1, "fallback": "Abort" }
}
],
"budget": {
"total_per_run": 15000,
"cost_estimate": "~$0.10"
}
}
}
}