Developers

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 activity
  • Abort — 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/name or @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