Scheduler CLI

Schedule agents to run continuously 24/7 at regular intervals

The Scheduler CLI enables 24/7 autonomous agent operations by running agents at regular intervals.

Quick Start

With Direct Prompt (No YAML needed)

bash
# Schedule with a simple prompt
praisonai schedule "Check AI news and summarize" --interval hourly

With agents.yaml

bash
# Schedule using agents.yaml configuration
praisonai schedule agents.yaml

Installation

bash
pip install praisonai praisonaiagents
export OPENAI_API_KEY=your_key_here

Deliver Scheduled Results to a Chat Channel

Route each successful scheduled run to Telegram, Discord, Slack, or WhatsApp — no gateway required.

Diagram

🤖 Agent

⏰ AgentScheduler
deliver=telegram:123456

📮 DeliveryRouter

✅ Telegram

Set a deliver token from Python, YAML, or the CLI:

Python

python
from praisonaiagents import Agent
from praisonai.scheduler import AgentScheduler

agent = Agent(name="Briefer", instructions="Summarise this morning's news in 5 bullets.")
AgentScheduler(agent, task="Morning brief", deliver="telegram:123456").start("every 1h")

YAML

yaml
schedule:
  every: "1h"           # alias for `interval`
  deliver: "telegram:123456"

CLI

bash
praisonai schedule agent.yaml --interval 1h --deliver telegram:123456
# or
praisonai schedule agent.yaml -i 1h -d telegram:123456

Token Grammar

The deliver token uses DeliveryTarget.parse. Whitespace is stripped and origin/all are case-insensitive.

TokenMeaning
"" / omittedNo delivery (default)
telegramPlatform’s home channel (router resolves)
telegram:123456Explicit channel
telegram:123:789Explicit channel + thread (platform:channel_id:thread_id)
originPersisted job origin — resolves on the lightweight path when ScheduleJob.origin is set; otherwise logs a warning and skips
allFan out to every registered bot — requires the full BotOS gateway

Optional Dependency

Delivery goes through the praisonai-bot package.

bash
pip install "praisonai[bot]"

If it isn’t installed, the scheduled run still succeeds — a single warning is logged and delivery is a no-op.

Delivery outcomes

Every scheduled run now resolves to one of four typed outcomes — a failed delivery is reported through on_failure instead of being a silent success (PraisonAI PR #4476). This applies to both the scheduled loop and the one-time execute_once() path.

OutcomeWhen it firesCallbackCounted as
DELIVEREDRouter accepted the sendon_success(result)delivered_deliveries
SUPPRESSEDWhole output is exactly NO_REPLY / [SILENT] / SILENTon_success(result)Neither (recorded non-outcome)
NOT_CONFIGUREDNo deliver= target was seton_success(result)Neither
UNDELIVEREDdeliver=True returned False, wrapper couldn’t build (e.g. praisonai-bot missing), token unresolvable, delivery raisedon_failure("scheduled result could not be delivered")undelivered_deliveries

See Scheduler Delivery → Delivery outcomes for the full flow, stats fields, and the MESSAGE_UNDELIVERED hook.

Running schedule tick without a live gateway

praisonai schedule tick runs each due job once and delivers its deliver: target — you can schedule it straight from OS cron / CI without keeping any long-running gateway process alive. When no live gateway is wired, delivery falls back to a stateless, token-authenticated standalone sender for Telegram / Slack / Discord.

The minimum for a Telegram cron entry is two env vars — a bot token plus a default channel:

bash
# crontab -e
TELEGRAM_BOT_TOKEN=123456:AAE...
TELEGRAM_HOME_CHANNEL=-100555
0 8 * * * cd /srv/praisonai && praisonai schedule tick

Or drop the HOME_CHANNEL and target the chat explicitly on the job (deliver: telegram:<chat_id>) — the token env is still required.

See Out-of-process delivery for the env-var table, chat-id resolution order, and per-platform failure modes.

Delivery outcomes

Every scheduled run resolves to one of four typed outcomes, and a failed delivery is no longer a silent success — an undelivered result fires on_failure("scheduled result could not be delivered") and bumps undelivered_deliveries. This applies to both the scheduled loop and the one-time execute_once().

OutcomeWhen it firesCallbackCounted as
DELIVEREDRouter accepted the sendon_success(result)delivered_deliveries
SUPPRESSEDWhole output is exactly NO_REPLY / [SILENT] / SILENTon_success(result)Neither (recorded non-outcome)
NOT_CONFIGUREDNo deliver= target was seton_success(result)Neither
UNDELIVEREDdeliver=True returned False, wrapper couldn’t build, token unresolvable, delivery raisedon_failure("scheduled result could not be delivered")undelivered_deliveries

See Scheduler Delivery → Delivery outcomes for the full flow, stats, and MESSAGE_UNDELIVERED hook.

Async Delivery

AsyncAgentScheduler accepts the same deliver= parameter and dispatches delivery through asyncio.to_thread because the shared helper uses the sync bridge — it never raises and never blocks the event loop.

python
import asyncio
from praisonaiagents import Agent
from praisonai.scheduler import AsyncAgentScheduler

async def main():
    agent = Agent(name="Briefer", instructions="Summarise today's AI news in 5 bullets.")
    scheduler = AsyncAgentScheduler(agent=agent, task="Morning brief", deliver="telegram:123456")
    await scheduler.start("hourly")

asyncio.run(main())

Delivery Best Practices

Prefer platform:channel_id for reliable routing

A bare platform token depends on the router resolving the platform’s home channel. Use telegram:123456 to target an explicit chat and avoid ambiguity.

Keep the token in YAML for reproducibility

Store schedule.deliver in YAML so scheduled jobs are reproducible. Override with -d only for one-off runs.

origin resolves on the lightweight path; all needs the gateway

origin is resolved from the persisted ScheduleJob.origin — safe on the lightweight scheduler path (it delivers back to the same channel/thread the request came in on). Only all still requires the full BotOS gateway (it enumerates every registered bot); setting all on the lightweight path logs a warning and skips delivery.


Multi-user isolation (optional)

Every scheduler store call — list, get_by_name, remove_by_name — accepts an optional principal= argument that scopes the operation to a single resolved end-user identity:

python
from praisonaiagents.scheduler import get_default_store

store = get_default_store()

# Only Alice's jobs
alice_jobs = store.list(principal="telegram:12345")

# Cross-owner lookup / delete are refused
store.get_by_name("morning-brief", principal="bob")     # -> None if owned by alice
store.remove_by_name("morning-brief", principal="bob")  # -> False; job intact

ScheduleJob.principal round-trips through to_dict/from_dict and is omitted when None, so existing on-disk store files are byte-identical.


PM2-Style Daemon Commands

Start Scheduler

With a Task Prompt

bash
# Start as background daemon
praisonai schedule start <name> "Your task" --interval hourly

# With all options
praisonai schedule start my-agent "Monitor logs" \
  --interval "*/30m" \
  --timeout 120 \
  --max-cost 2.00 \
  --max-retries 3

# Examples
praisonai schedule start news-bot "Check AI news" --interval hourly
praisonai schedule start health-check "Monitor system" --interval "*/15m"
praisonai schedule start test-agent "Count to 5" --interval "*/10s"

With a Recipe

bash
# Schedule a recipe
praisonai schedule start news-monitor --recipe news-analyzer --interval hourly

# Recipe with custom interval
praisonai schedule start daily-report --recipe report-generator --interval daily

# Recipe with all options
praisonai schedule start my-scheduler \
    --recipe my-recipe \
    --interval "*/6h" \
    --timeout 600 \
    --max-cost 2.00 \
    --max-retries 3

List Schedulers

bash
# Show all running schedulers
praisonai schedule list

# Output example:
# Name                 Status     PID      Interval     Task
# ================================================================
# news-bot             🟢 running  12345    hourly       Check AI news
# health-check         🟢 running  12346    */15m        Monitor system
# 
# Total: 2 scheduler(s)

View Logs

bash
# View last 50 lines
praisonai schedule logs <name>

# Follow logs in real-time
praisonai schedule logs <name> -f

# Examples
praisonai schedule logs news-bot
praisonai schedule logs news-bot --follow

Stop Scheduler

bash
# Graceful shutdown
praisonai schedule stop <name>

# Example
praisonai schedule stop news-bot

Restart Scheduler

bash
# Restart with same configuration
praisonai schedule restart <name>

# Example
praisonai schedule restart news-bot

Delete Scheduler

bash
# Remove from list (must be stopped first)
praisonai schedule delete <name>

# Example
praisonai schedule delete news-bot

Describe Scheduler

bash
# Show detailed information
praisonai schedule describe <name>

# Shows: PID, status, uptime, executions, cost, config, logs path
# Note: Async daemons (schedule start) now correctly persist executions and cost to disk (PR #1857)

Legacy Foreground Mode

For quick testing or one-off runs, use foreground mode:

bash
# Direct prompt (runs in foreground)
praisonai schedule "Your task" --interval hourly

# YAML mode (runs in foreground)
praisonai schedule agents.yaml

YAML Configuration Example:

yaml
framework: praisonai

agents:
  - name: "AI News Monitor"
    role: "Technology News Analyst"
    goal: "Monitor and summarize AI news"
    instructions: "Search for latest AI developments"
    tools:
      - search_tool
    verbose: true

task: "Search for latest AI news and provide top 3 stories"

schedule:
  interval: "hourly"
  max_retries: 3
  run_immediately: true
  timeout: 60
  max_cost: 1.00

verbose: true on a scheduled agent enables the Agent’s verbose output mode (equivalent to output: "verbose"). Omit it or set it to false for silent runs.

Tool resolution in scheduled agents.yaml

A scheduled agents.yaml resolves its tools: list through the same ToolResolver that praisonai run uses (via AgentsGenerator._build_tools_dict). Referring to duckduckgo, internet_search, or any other resolver-known tool inside a scheduled config gives the agent that real tool at runtime, so CLI + YAML + Python all behave identically.

yaml
# scheduled_agent.yaml — schedule via `praisonai schedule ...`
agents:
  - name: NewsChecker
    instructions: Summarise today's AI news in three bullet points
    tools:
      - duckduckgo         # resolved via ToolResolver, not the old hardcoded search_tool
      - internet_search

Run YAML in foreground:

bash
praisonai schedule agents.yaml

Press Ctrl+C to stop. Shows final statistics:

Execution stats - Total: 12, Success: 11, Failed: 1
Total cost: $0.0056
Runtime: 3600.5s

Wall-clock cron & restart recovery

praisonai schedule daemons handle cron: schedules on wall-clock time and survive restarts:

  • Time-of-day accuracy — --interval "cron:0 9 *" fires at 09:00 every day in the schedule’s resolved timezone, not at whatever o’clock the daemon started. cron: resolves the zone from the schedule tz → the instance default → PRAISONAI_SCHEDULE_TIMEZONE, and falls back to UTC when none is set. See Schedule Tools → Timezones.
  • Downtime catch-up (exactly once) — a slot missed while the daemon was stopped/killed/scaled-to-zero runs once on resume, then re-anchors to the next future occurrence.
  • State file — ~/.praisonai/schedulers/<name>.json now carries last_run_at alongside executions and cost. praisonai schedule stop + praisonai schedule start picks up the schedule exactly where it left off.

cron: schedules use the croniter engine, which is bundled with praisonaiagents (installed transitively when you install praisonai) — no separate install step. The core parse_schedule() refuses a cron schedule at creation with a clear ValueError if the engine is somehow missing on a stripped install, so a cron schedule is never accepted and silently skipped. The praisonai schedule daemon ticker is a separate wrapper path: on a stripped install without croniter it falls back to a coarse interval anchored to process start and logs a one-time warning.

Introduced in PraisonAI PR #3527.

Example — daily morning brief that survives restarts:

bash
praisonai schedule start morning-brief "Summarise today's news in 5 bullets" \
  --interval "cron:0 9 * * *" \
  --deliver telegram:123456

Stop the daemon at 14:00, restart at 15:00 — today’s 09:00 slot already ran (so no catch-up), and the next fire is tomorrow 09:00, not 15:00 + 24h.

Daemon state persistence

The daemon writes catch-up state to ~/.praisonai/schedulers/<name>.json. As of PraisonAI #4537:

  • Writes are atomic — the state file is written to a temp file, fsync‘d, then renamed into place with os.replace. A crash mid-write can no longer produce a truncated JSON file that silently disables cron catch-up on next start.
  • Corrupt / unreadable state files log a warning — you’ll see Corrupt scheduler state ... or Cannot access scheduler state ... in the logs, then the daemon re-anchors from process start (previous behaviour with no log).

If a scheduled run misses its slot after a crash, grep the logs for those two messages first. The async scheduler shares the same behaviour — see Async Scheduler → Daemon state persistence.

The schedule store (FileScheduleStore) has matching crash-safe guarantees for schedule data and run history — see Async Scheduler → Store write durability.

Storage Locations

  • Schedule data: ~/.praisonai/config.yaml (under the schedules key)
  • Log files: ~/.praisonai/logs/*.log

Features

✅ PM2-style daemon management – No nohup needed ✅ Process persistence – State saved to disk ✅ Easy lifecycle control – start/stop/restart/list ✅ Centralized logging – Auto-rotation, follow mode ✅ Graceful shutdown – SIGTERM with SIGKILL fallback ✅ Cost monitoring – Budget limits with $1.00 default ✅ Timeout protection – Prevent runaway executions ✅ Auto cleanup – Dead processes removed automatically

Schedule Intervals

FormatIntervalDescription
hourly3600sEvery hour
daily86400sEvery 24 hours
weekly604800sEvery 7 days
*/30m1800sEvery 30 minutes
*/6h21600sEvery 6 hours
*/5s5sEvery 5 seconds (testing)
36003600sCustom seconds
cron:0 9 *wall-clockFires at real time-of-day. Uses the croniter engine bundled with praisonaiagents.
weekdays at 9amwall-clockNatural language → cron:0 9 1-5. Mon–Fri.
every day at 8amwall-clockNatural language → cron:0 8 *. Daily.

--interval also accepts natural-language forms — a bare clock time (at 9am) is a one-shot, and any recurring form (every …, daily …, or a day-of-week name) becomes a cron schedule:

bash
praisonai schedule "Post standup" --interval "weekdays at 9am"
praisonai schedule "Send digest"  --interval "every day at 8am"

See Async Agent Scheduler → Natural language for the full grammar.

Examples

Example 1: Simple Prompt Scheduling

Quick news check every hour:

bash
praisonai schedule "Search for latest AI news and summarize top 3 stories" --interval hourly --verbose

System monitoring every 15 minutes:

bash
praisonai schedule "Check system health and disk space" --interval "*/15m"

With budget limit:

bash
praisonai schedule "Analyze market trends" \
  --interval "*/30m" \
  --max-cost 0.50 \
  --timeout 120

Save Configuration

bash
# Export scheduler config to YAML
praisonai schedule save <name> [output.yaml]

# Example
praisonai schedule save news-bot news-config.yaml

Command Reference

Daemon Commands

CommandDescriptionExample
start <name> "task"Start scheduler as daemonpraisonai schedule start my-bot "Task"
listList all schedulerspraisonai schedule list
logs <name> [--follow]View logspraisonai schedule logs my-bot --follow
stop <name>Stop schedulerpraisonai schedule stop my-bot
restart <name>Restart schedulerpraisonai schedule restart my-bot
delete <name>Remove from listpraisonai schedule delete my-bot
describe <name>Show detailspraisonai schedule describe my-bot
save <name> [file]Export to YAMLpraisonai schedule save my-bot config.yaml

Options

OptionTypeDescriptionDefaultExample
--intervalstringSchedule intervalhourlyhourly, daily, weekly, */30m
--max-retriesintMax retry attempts33, 5
--timeoutintMax execution time (seconds)None60, 120
--max-costfloatBudget limit in USD$1.001.00, 5.00
--deliver, -dstringDeliver each result to a chat target (overrides YAML/recipe)""telegram:123456, telegram
--verbose, -vflagEnable verbose loggingFalse–

Notes:

  • Default budget is $1.00 for safety. Set to higher value or null in YAML to disable.
  • Use --verbose to see detailed logs. Without it, output is clean for background running.

Example 2: News Monitoring with YAML (Advanced)

agents.yaml:

yaml
framework: praisonai

agents:
  - name: "AI News Monitor"
    role: "Technology News Analyst"
    instructions: "Search and summarize latest AI news"
    tools:
      - search_tool

task: "Search for latest AI news and provide top 3 stories"

schedule:
  interval: "hourly"
  max_retries: 3
  run_immediately: true

Run:

bash
praisonai schedule agents.yaml

Example 2: Data Collection (Every 30 Minutes)

agents.yaml:

yaml
framework: praisonai

agents:
  - name: "Data Collector"
    role: "Data Analyst"
    instructions: "Collect and analyze market data"
    tools:
      - search_tool

task: "Collect latest market data and identify trends"

schedule:
  interval: "*/30m"
  max_retries: 5
  run_immediately: false

Run with override:

bash
# Override to run every 15 minutes instead
praisonai schedule agents.yaml --interval "*/15m"

Example 3: With Budget and Timeout Limits

agents.yaml:

yaml
framework: praisonai

agents:
  - name: "Budget-Controlled Agent"
    role: "Worker"
    instructions: "Process data efficiently"
    tools:
      - search_tool

task: "Process and analyze data"

schedule:
  interval: "*/5m"
  max_retries: 3
  run_immediately: true
  timeout: 120              # Max 2 minutes per execution
  max_cost: 0.50            # Stop after $0.50 spent

Run:

bash
praisonai schedule agents.yaml --verbose

Output:

Budget limit: $0.50
Timeout per execution: 120s
...
Run cost: $0.0034 (model=gpt-4o-mini, in=2100, out=480). Total: $0.0034 / $0.5
...
Budget limit reached: $0.5001 >= $0.50
Stopping scheduler to prevent additional costs

Example 4: Testing with Short Interval

bash
# Test with 10-second interval
praisonai schedule agents.yaml --interval "*/10s" --verbose

Python API

For programmatic control, use the async-native Python API:

Async Agent Scheduler (Recommended)

python
import asyncio
from praisonaiagents import Agent
from praisonai.scheduler import AgentScheduler

async def main():
    # Create agent
    agent = Agent(
        name="NewsChecker",
        instructions="Check latest AI news",
    )

    # Create scheduler using canonical import
    scheduler = AgentScheduler(
        agent=agent,
        task="Search for latest AI news"
    )

    # Start (runs every hour)
    scheduler.start("hourly", max_retries=3, run_immediately=True)

    # Keep running
    try:
        import time
        time.sleep(3600)  # Run for 1 hour
    except KeyboardInterrupt:
        pass
    finally:
        scheduler.stop()
        stats = scheduler.get_stats()
        print(f"Executions: {stats['execution_count']}, Success: {stats['success_count']}")

asyncio.run(main())

Features

Core Features

  • Interval-based scheduling: Run agents at regular intervals
  • Background execution: Runs in daemon thread, won’t block terminal
  • Automatic retry: Exponential backoff + jitter, capped at 300s, shared between sync & async
  • Graceful shutdown: Clean stop with Ctrl+C
  • YAML configuration: Simple configuration in agents.yaml
  • CLI overrides: Override any setting from command line

Safety Features

  • ⏱️ Timeout Protection: Prevent runaway executions
  • 💰 Cost Monitoring: Real-time cost tracking with budget limits
  • 📊 Statistics Tracking: Monitor execution success rates, costs, and runtime
  • 🛡️ Budget Protection: Auto-stops when cost limit reached — enforced on the scheduled loop, run_immediately=True, and execute_once() (PR #5766).
  • 🔄 Retry Logic: Exponential backoff prevents rapid failures

Output

The scheduler provides detailed logging with cost tracking:

Starting agent scheduler: AI News Monitor
Task: Search for latest AI news
Schedule: hourly (3600s interval)
# For a cron schedule the line reads instead:
# Schedule: cron:0 9 * * * (wall-clock cron)
Timeout per execution: 60s
Budget limit: $1.00
Agent scheduler started successfully

[2025-12-22 10:00:00] Starting scheduled agent execution
Attempt 1/3
Agent execution successful on attempt 1
Result: [agent output]
Run cost: $0.0034 (model=gpt-4o-mini, in=2100, out=480). Total: $0.0034 / $1.0
Next execution in 3600 seconds (1.0 hours)

Callbacks

Both schedulers accept on_success and on_failure callbacks in the constructor. Callbacks may be sync or async functions; a raising callback is logged and swallowed — it will not stop the scheduler.

  • on_success(result) — called with the agent’s return value after a successful run.
  • on_failure(exc) — called with the final Exception after all retries are exhausted.
  • (Previously sync passed a formatted string; as of PR #1474 both sync and async
  • pass the exception object.)
python
def success_callback(result):
    print(f"Success: {result}")

def failure_callback(exception):
    print(f"Failed: {exception}")

scheduler = AgentScheduler(
    agent=agent,
    task="Check news",
    on_success=success_callback,
    on_failure=failure_callback
)

Statistics

CLI Daemon: Use praisonai schedule describe <name> for detailed stats

Python API (AsyncAgentScheduler):

python
stats = await scheduler.get_stats_async()
# Returns:
{
    "is_running": True,
    "total_executions": 10,
    "successful_executions": 9,
    "failed_executions": 1,
    "success_rate": 90.0,
    "total_cost_usd": 0.0234,
    "remaining_budget": 0.9766,
    "runtime_seconds": 3601.5,
    "cost_per_execution": 0.0023,
    "delivered_deliveries": 8,
    "undelivered_deliveries": 1,
}

SUPPRESSED and NOT_CONFIGURED runs count as successful_executions but do not count as either delivered_deliveries or undelivered_deliveries — each counter is bumped only by its own outcome. See the per-outcome counter table in Scheduler Delivery.

On stop (Ctrl+C)

🛑 Stopping scheduler…

📊 Final Statistics: Total Executions: 5 Successful: 5 Failed: 0 Success Rate: 100.0%

✅ Agent stopped successfully

## Error Handling

The scheduler retries failed executions with exponential backoff + jitter:

- **Attempt 1:** Run immediately.
- **On failure:** Wait `backoff_delay(attempt)` seconds, then retry.
- Delay formula: `min(max(30, 2 ** attempt), 300)` seconds, with ±10% jitter.
- **Cap:** 300s between attempts. **Floor:** ~27s.
- Sync and async schedulers now share the same `backoff_delay()` implementation as of [PR #1673](https://github.com/MervinPraison/PraisonAI/pull/1673), not just the same formula.

`max_retries` is the **total** number of attempts (including the first run).
`max_retries=3` → 1 initial attempt + up to 2 retries.

## Stopping the Scheduler

### Interruptible backoff (PR #1673)

Calling `scheduler.stop()` during a retry backoff window now returns within milliseconds. Previously a stop signal could wait out the full backoff delay (up to ~300s).

The async scheduler reached the same parity in [PraisonAI #4537](https://github.com/MervinPraison/PraisonAI/pull/4537) — see [Async Scheduler → Graceful Shutdown](/features/async-scheduler#best-practices).

### Cross-platform timeout (PR #1741)

Execution timeouts now work on all platforms including Windows and can run safely in worker threads (FastAPI background tasks, the bot adapter, Streamlit). Previously timeouts were Unix/Linux/Mac only and failed in worker thread contexts.

```python
import signal
from praisonai.scheduler import AgentScheduler

scheduler = AgentScheduler(agent, "Monitor logs")
scheduler.start("hourly", max_retries=3)

# Set up signal handler
def handle_sigint(signum, frame):
    print("Ctrl+C pressed - stopping scheduler...")
    scheduler.stop()  # Returns immediately, even during backoff
    exit(0)

signal.signal(signal.SIGINT, handle_sigint)

CLI Commands

Use the daemon management commands:

bash
# Graceful shutdown
praisonai schedule stop <name>

# Force kill if needed
praisonai schedule delete <name>

Python API

python
# Graceful async shutdown
await scheduler.stop()  # Waits up to 30s for current execution

Foreground Mode

Press Ctrl+C to stop gracefully. The scheduler will:

  1. Set stop event
  2. Wait for current execution to complete
  3. Log final statistics
  4. Exit cleanly

Troubleshooting

TypeError: Agent.__init__() got an unexpected keyword argument 'verbose'

The scheduler’s YAML loader used to forward verbose: to Agent(...) as a keyword argument, but Agent does not accept verbose. Every scheduled agents.yaml — with or without a verbose: key — raised this error on startup.

Fix: upgrade praisonai to a version that includes PR #4348. After the fix, verbose: true in YAML is translated to Agent(output="verbose") internally; false (or omitted) becomes output="silent".

See Also

  • Async Agent Scheduler – Python async-native scheduler API
  • Delivery Config – The DeliveryRouter machinery scheduled results reuse (origin / all need the full gateway)
  • Scheduled Run Policy – Tool scoping, prompt scanning, and output auditing for unattended runs (configure via the scheduler: block in gateway.yaml, or via RunPolicy in Python for non-gateway embeddings)
  • Planning Mode – Add planning to scheduled agents
  • Memory – Enable memory for scheduled agents
  • Tools – Add custom tools to agents
  • Examples – Working examples