Running Background Agents and Scheduled Tasks
Run long tasks without blocking your main session, and schedule recurring work so Pi can keep checking, reviewing, or cleaning up in the background. This is useful when you want to keep coding while another agent works, or when you want a workflow to run on a timer.
Prerequisites
- A running Pi session in your project repository
- A TUI session if you want to use the fullscreen status overlays
gitavailable if your background task depends on repository state- If you use ACPX-backed models for detached work,
internal_operations_providerandinternal_operations_modelmay be required. See Configuration & Settings for details.
Quick Example
Start a background agent with a plain-English request:
Run the
security-auditoron thesrc/directory in the background. Let me know when it's done.
Schedule a recurring task with /cron:
/cron Run the test-automator every 30 minutes
Use these when you want non-blocking work right away: one-off jobs through natural language, recurring jobs through /cron.
Step-by-Step
- Start a background job.
Ask Pi to run a specialist in the background:
Run the
security-auditoron thesrc/directory in the background. Let me know when it's done.
Pi can keep your main session free while the background job runs and report back when it finishes.
- Monitor running async work.
Open the async overlay:
/async-status
This shows queued and running jobs, elapsed time, and live output. Press Enter to inspect a job, press x to kill the focused job, press Space to multi-select rows and x again to kill the whole selection, or press X to immediately kill every running and queued job (no confirmation prompt).
- Stop async work when needed.
Use the same /async-status overlay:
Spacetoggles rows into a selection,xkills the whole selection at once.Ximmediately kills every running and queued job. There is no confirmation prompt.
Tip: If you only need to stop one job,
xon the focused row is safer because it shows the exact running entries before you kill them.
- Create a recurring schedule.
Use /cron with natural language:
/cron Every day at 9:00 AM, run the git-expert to generate a daily summary.
Pi turns that into a recurring task for the current session.
- Review or remove scheduled tasks.
List tasks in the current session:
/cron list
List tasks across active Pi sessions:
/cron list-all
Remove a task by ID:
/cron remove 1
/cron rm <id> and /cron delete <id> are accepted aliases. In the cron overlays, you can also press x to remove the selected task.
- Make a schedule survive a Pi restart.
Prefix any /cron request with --persist:
/cron --persist Every day at 9:00 AM, run the git-expert to generate a daily summary.
Persisted tasks are written to .pi/cron/crons.json in the project and reloaded by later sessions.
Advanced Usage
Pick the Right Monitoring Command
| Goal | Command | What you get |
|---|---|---|
| Watch async jobs | /async-status |
Fullscreen list, live output, kill with x |
| Kill the focused job | /async-status → x |
Kills the highlighted job |
| Kill several jobs | /async-status → Space, Space, x |
Kills every multi-selected job |
| Kill every running job | /async-status → a |
Confirmation prompt, then kills all running and queued jobs |
| See local schedules | /cron list |
Fullscreen list of this session's recurring tasks |
| See only persisted schedules | /cron --persist list |
Filters the overlay down to project-scoped crons |
| See all session schedules | /cron list-all |
Cross-session view of active cron files |
| Remove a known cron | /cron remove <id> |
Direct removal by task ID |
Pick the Model for Background Work
Detached agents are resolved through the same chain as any other subagent
(explicit > agent_overrides[name] > agent frontmatter > agent_provider/agent_model > parent),
so a plain agent_model setting is all you need to pin background work to a specific model.
It is a string setting, default "", meaning "inherit the parent session's model".
To pin one schedule without moving the whole session, name the model in the request:
/cron --persist Every hour, check upstream releases using openrouter/anthropic/claude-opus-5.
cron_manage stores that on the task and passes it as the subagent explicit override for
that run only. A bare model-id keeps the normal provider resolution chain. An unpinned cron
uses the worker's own chain (agent_overrides[worker] → worker frontmatter →
agent_provider/agent_model) and does not inherit the session model. Pinning an acpx-*
provider does not work — child processes cannot register those providers, so the cron runs on
internal_operations_provider/internal_operations_model when both are set, and is skipped
with a logged error when they are not (see ACPX Compatibility below).
Keep Background Context Between Runs
If you want a background agent to remember previous work, ask Pi to persist that session:
Run the code reviewer in the background and persist its session so it remembers previous feedback.
This is most useful for iterative review loops and repeated follow-up work. See Automating Code Reviews for a full review workflow.
Use Fire-and-Forget for Maintenance Jobs
For maintenance tasks where you do not want a follow-up result injected back into chat, ask for fire-and-forget behavior:
Run a background memory cleanup task as fire-and-forget.
This is a good fit for housekeeping work where a completion notification is enough. See Background Memory Consolidation (Dreaming) for a concrete example.
Understand Cron Lifetime
Cron tasks have two scopes, chosen by the --persist flag:
| Scope | Stored in | Survives Pi exit | Runs from |
|---|---|---|---|
| session (default) | The session store under .pi/tmp/ |
No | That session only |
project (--persist) |
.pi/cron/crons.json in the project |
Yes | Any session opened in that project |
Session-scoped tasks survive /reload inside the running process but are deleted on a real exit. Project-scoped tasks are reloaded by later sessions, and a leader lease in the same directory guarantees only one session executes a given task — other sessions show it as project (waiting for leader).
Note: If you need to confirm what is still scheduled, run
/cron listor/cron list-allafter reconnecting instead of assuming an older schedule is still active.Tip:
/cronwith free text does not schedule anything by itself. It re-enters the conversation with a request to call thecron_managetool, andpersistis set totrueonly when you passed--persist. Schedules must have aninterval_secondsof at least 10, andat_minuteis only valid together withat_hour.modelacceptsprovider/model-idor a baremodel-id; leaving it out uses the worker's normal model resolution.
ACPX Compatibility
Detached LLM work is more restricted when your parent session is using an acpx-* provider. Child Pi processes launched as async agents skip ACPX provider registration, so Pi decides between three outcomes:
| Case | Behavior |
|---|---|
Parent is native or cli-* |
Async work runs detached as normal. |
Parent is acpx-*, work is not must-async |
The request is coerced to run synchronously instead. |
Parent is acpx-*, work is must-async (dreaming, fireAndForget) |
Runs detached on internal_operations_provider/internal_operations_model; if those are unset, the work is skipped with a warning. |
Set internal_operations_provider and internal_operations_model to cover that last case. Both are string settings, default "", and the provider must not itself be an acpx-* id.
See ACPX Provider Integration and Configuration & Settings for the exact setup.
Troubleshooting
- Async job does not start: Confirm you asked for it in the background (
async: true) and that you ended your turn — Pi delivers results automatically and does not expect a polling loop. Agents declared async-only forceasync: trueon a native provider and refuse chain mode. - Job starts but immediately skips: If your session is using an ACPX-backed provider, configure
internal_operations_providerandinternal_operations_modelbefore retrying. - You want the result to update a task automatically: Pass a numeric
taskIdfrom the task tools. It is optional; when set, the finished job marks that taskcompletedfor you. - You want live output but nothing appears: Use
/async-statusfrom a TUI session. The fullscreen overlay is the supported live-view interface. - A cron task seems stuck or outdated: Run
/cron listor/cron list-all, then remove the old task and create a fresh one. - You want browser-based monitoring instead of terminal overlays: See Using the Web Dashboard for details.