Agents
Creating an Agent
An Agent is the most powerful extension in Nebo. It's a complete agent persona with scheduled workflows, event-driven automation, and skill dependencies — all bundled into two files.
The Two-File Structure
chief-of-staff/
├── AGENT.md ← Persona (pure prose)
└── agent.json ← Operational config (bindings, triggers, activities)
AGENT.md — The Persona
Pure markdown. No frontmatter required. This becomes the agent's identity — it replaces the default system prompt when the agent is active.
# Chief of Staff
You manage the executive's daily rhythm. Every morning at 7 AM, you prepare
a concise briefing covering calendar, emails, and market news.
## Communication Style
- Direct and efficient
- Lead with the most important item
- Flag anything requiring immediate decision
## Boundaries
- Never commit the executive to meetings without confirmation
- Always include source links for market data
Write it as a job description. Define how the agent thinks, communicates, and what it should never do.
agent.json — The Operational Config
This is where the real power lives. It defines what happens and when:
{
"workflows": {
"morning-briefing": {
"trigger": { "type": "schedule", "cron": "0 7 * * *" },
"description": "Daily morning briefing",
"activities": [
{
"id": "gather",
"intent": "Gather news and calendar events",
"skills": ["@nebo/skills/briefing-writer@^1.0.0"],
"model": "claude-sonnet-4",
"steps": [
"Fetch top headlines from configured news sources",
"Check today's calendar for meetings and deadlines",
"Summarize into a 3-paragraph briefing"
],
"token_budget": { "max": 4096 },
"on_error": { "retry": 2, "fallback": "NotifyOwner" }
}
],
"budget": { "total_per_run": 5000, "cost_estimate": "$0.01" },
"emit": "briefing.ready"
},
"day-monitor": {
"trigger": { "type": "heartbeat", "interval": "30m", "window": "08:00-18:00" },
"description": "Monitor for important changes during work hours"
},
"interrupt": {
"trigger": { "type": "event", "sources": ["calendar.changed", "email.urgent"] },
"description": "React to calendar changes and urgent emails",
"activities": [
{
"id": "assess",
"intent": "Assess the change and notify if action needed",
"model": "claude-haiku-4",
"steps": ["Evaluate urgency", "Draft notification if needed"]
}
]
},
"ad-hoc-report": {
"trigger": { "type": "manual" },
"description": "Generate an on-demand status report"
}
},
"skills": ["@nebo/skills/briefing-writer@^1.0.0"],
"pricing": { "model": "monthly_fixed", "cost": 47.0 },
"defaults": {
"timezone": "user_local",
"configurable": ["workflows.morning-briefing.trigger.cron"]
}
}
Five Trigger Types
Agents own the schedule. Each binding in workflows has exactly one trigger:
Schedule (Cron)
Fires at predictable times using standard cron syntax:
{ "type": "schedule", "cron": "0 7 * * *" }
The cron scheduler ticks every 60 seconds. The cron field uses standard 5-field cron syntax (minute, hour, day-of-month, month, day-of-week).
Heartbeat
Recurring interval, optionally restricted to a time window:
{ "type": "heartbeat", "interval": "30m", "window": "08:00-18:00" }
Without activities, heartbeat bindings run as a run_chat() call — the agent checks in using the heartbeat content. With activities, the full workflow engine executes.
Event
Fires in real-time when something happens in the system:
{ "type": "event", "sources": ["calendar.changed", "email.urgent"] }
Must have at least one source. Events are dispatched by the EventDispatcher and execute via WorkflowManager.run_inline().
Watch
Runs a long-lived plugin process that outputs NDJSON to stdout. Each JSON line triggers bound activities and optionally auto-emits into the EventBus:
{
"type": "watch",
"plugin": "gws",
"event": "email.new",
"restart_delay_secs": 5
}
| Field | Required | Default | Description |
|---|---|---|---|
plugin |
Yes | -- | Plugin slug (e.g., "gws") |
event |
No | -- | Plugin event name. Resolves command from the plugin manifest |
command |
No | "" |
CLI args for the plugin binary. Required if event is not set |
restart_delay_secs |
No | 5 |
Seconds before restarting on crash |
When event is set, the CLI command is resolved from the plugin's events array in its plugin.json -- you don't hardcode it. Each NDJSON line auto-emits into the EventBus as {plugin}.{event} (e.g., gws.email.new), so other agents can subscribe via event triggers.
Event-only watches (no activities) are valid -- they relay plugin events into the EventBus for other agents to consume:
{
"email-relay": {
"trigger": { "type": "watch", "plugin": "gws", "event": "email.new" },
"description": "Relay new emails into the EventBus"
}
}
For details on plugin events and the NDJSON protocol, see Creating a Plugin.
Manual
Triggered explicitly by the user:
{ "type": "manual" }
Useful for on-demand reports or ad-hoc tasks.
Inline Activities
Each binding can define activities — the actual work to be done. An activity is an LLM agent task with full tool access:
{
"id": "gather",
"intent": "Gather news and calendar events",
"skills": ["@nebo/skills/briefing-writer@^1.0.0"],
"mcps": ["news-api"],
"cmds": [],
"model": "claude-sonnet-4",
"steps": [
"Fetch top headlines",
"Check today's calendar",
"Summarize into briefing format"
],
"token_budget": { "max": 4096 },
"on_error": { "retry": 2, "fallback": "NotifyOwner" }
}
| Field | Type | Description |
|---|---|---|
id |
string | Unique within the binding |
intent |
string | What this activity should accomplish |
skills |
string[] | Skill references for context injection |
mcps |
string[] | MCP server references |
cmds |
string[] | Shell commands to enable |
model |
string | AI model to use (claude-sonnet-4, claude-haiku-4, etc.) |
steps |
string[] | Step-by-step instructions |
token_budget |
object | { "max": 4096 } — per-activity token limit |
on_error |
object | { "retry": N, "fallback": "NotifyOwner"|"Skip"|"Abort" } |
Error handling fallbacks:
NotifyOwner— alert the user (default)Skip— continue to next activityAbort— stop the entire workflow
Bindings without activities are chat-only — the heartbeat system runs them as a simple agent chat with the agent's persona.
Emit Chains
A binding can emit a named event when it completes, triggering other bindings:
{
"morning-briefing": {
"trigger": { "type": "schedule", "cron": "0 7 * * *" },
"emit": "briefing.ready",
"activities": [...]
},
"distribute-briefing": {
"trigger": { "type": "event", "sources": ["chief-of-staff.briefing.ready"] },
"activities": [...]
}
}
Events are namespaced by the agent's slug at runtime: "agent-slug.briefing.ready". This lets you build multi-step pipelines where one workflow's output feeds into another.
Skill Dependencies
The top-level skills array declares what skills the agent needs. These must be qualified references:
"skills": [
"@nebo/skills/briefing-writer@^1.0.0",
"SKIL-RFBM-XCYT"
]
Valid formats:
- Qualified name:
@org/skills/nameor@org/skills/name@^version - Install code:
SKIL-XXXX-XXXX
When a user installs the agent, all skill dependencies are cascade-installed automatically.
AgentWorker Runtime
Each active agent gets its own AgentWorker — a per-agent runtime that:
- Registers heartbeat loops for heartbeat-triggered bindings
- Subscribes to events for event-triggered bindings
- Registers cron jobs for schedule-triggered bindings
- Handles the agent's lifecycle (activation, deactivation, updates)
The worker starts when the agent is activated and stops when it's deactivated.
Install Codes
Published agents receive a unique install code:
AGNT-XXXX-XXXX
Users paste this into Nebo to install the agent, its AGENT.md persona, agent.json config, and all skill dependencies in one step.
Testing Locally
Place your agent files in Nebo's agents directory:
- macOS:
~/Library/Application Support/nebo/user/agents/your-agent/ - Linux:
~/.local/share/nebo/user/agents/your-agent/ - Windows:
%APPDATA%/nebo/user/agents/your-agent/
Each agent directory must contain at minimum an AGENT.md. The agent.json is optional — without it, the agent is a persona-only agent with no automation.
Next Steps
- Agent Reference — complete schema for agent.json
- SKILL.md Reference — writing skills for your agent
- MCP Servers — connecting external tools via
mcps[] - Publishing — submit to the marketplace