Quick Start
1
Agent with background runner
2
Recipe in the background
3
Async callers (FastAPI / Jupyter)
Inside a running event loop (a FastAPI handler, a Jupyter cell, or any
async def), use arun_background — the sync run_background raises RuntimeError there instead of deadlocking:Features
- Async Execution: Run tasks without blocking
- Concurrency Control: Limit concurrent tasks
- Progress Tracking: Monitor task status
- Timeout Support: Set execution time limits
- Cancellation: Cancel running tasks
Configuration
Task Status
CLI Usage
Safe Defaults
Low-level API Reference
BackgroundRunner Direct Usage
Submitting Tasks
Task Management
Synchronous Job Manager
For simpler use cases, useBackgroundJobManager for synchronous job management:
Durability — survive a restart
An in-memory-only runner drops every in-flight job on a restart, including the promised deliver-back — pass astore= to persist jobs and recover them on boot.
Durability is opt-in: omit store= and behaviour is byte-for-byte as before (pure in-memory, zero overhead); supply a store and every state transition is persisted.
1
Enable persistence (opt-in)
2
Reconcile once at startup
Call
reconcile_on_start() once at boot, after wiring the deliver-back handler:New API surface
reconcile_on_start(redeliver) passes the persisted JobInfo to your redeliver callback — make it idempotent, since a raised exception means “retry on the next restart” (the job is left undelivered, never lost).
LOST jobs are age-evictable by cleanup_completed(max_age=...), so reconciled orphans don’t leak across restarts.Architecture
The sync wrappers andScheduleLoop bridge the gap between disconnected modules:
submit_sync()/submit_agent_sync()— let sync code submit background tasks without asyncio boilerplateScheduleLoop— polls for due jobs on a daemon thread and fires your callback
Sync Wrappers
For sync code (scripts, bot handlers,Agent.start() callbacks), use the sync-friendly methods that handle asyncio automatically:
submit_sync()
Submit any callable from synchronous code. A daemon event loop thread is created lazily on first call.submit_agent_sync()
Submit an Agent task from synchronous code. Resolves the agent’s callable (start → chat → run) automatically.
ScheduleLoop
ScheduleLoop bridges scheduled jobs to actual execution. It runs a daemon thread that polls get_due_jobs() and fires your callback. To skip ticks cheaply before the model turn runs, see pre_run in Schedule Tools.
Combined Example: Scheduler + Background
ScheduleLoop is the default SchedulerProviderProtocol — the in-process poll thread, also exported as InProcessScheduleProvider. For event-driven / serverless firing (webhook, systemd timer, cron, K8s CronJob), see Scheduler Providers.Error handling: If
on_trigger() raises an exception, it’s logged but not propagated — the loop continues with remaining jobs and future ticks.Zero Performance Impact
The background module uses lazy loading — no overhead when not used:Best Practices
Cap concurrent tasks
Cap concurrent tasks
Set
BackgroundConfig(max_concurrent_tasks=...) to match CPU and API rate limits — unbounded parallelism can exhaust tokens or file handles.Always await task.wait with a timeout
Always await task.wait with a timeout
Background work can hang on tool calls; pass
timeout= so your main loop can cancel or retry instead of blocking forever.Pair with ScheduleLoop for cron-style work
Pair with ScheduleLoop for cron-style work
Fire scheduled jobs into
BackgroundRunner.submit_agent_sync so reminders run without blocking the scheduler thread.Use async-jobs for HTTP clients
Use async-jobs for HTTP clients
When callers are external services, prefer the Async Jobs server instead of embedding
BackgroundRunner in app code.Related
Async Jobs
HTTP API for submitting and polling long-running agent jobs.
Schedule Tools
Let agents create reminders that trigger background runs.

