Background Tasks
Task orchestration, cron scheduling, and git worktree isolation for parallel agent work.
Background Tasks
Cersei includes built-in tools for task orchestration, scheduled jobs, and isolated workspaces. These let the model manage long-running work, schedule recurring prompts, and work in parallel git branches — all through the standard tool system.
Task System
Tasks track units of work across turns. The model creates a task, updates its status as work progresses, and stores the output when done. Tasks live in an in-memory registry (concurrent-safe DashMap) scoped to the session.
Task Lifecycle
TaskCreate → Pending → Running → Completed
↘ ↗
Failed
↘
StoppedTaskEntry
Prop
Type
Tools
TaskCreate
Creates a new task in Pending state.
Prop
Type
Returns: "Task 'abc12345' created: Fix the authentication bug"
Permission: Execute
TaskGet
Retrieve a task's current state.
Prop
Type
Returns: Status, description, output (if completed), timestamps.
Permission: ReadOnly
TaskUpdate
Update a task's status and/or output.
Prop
Type
Permission: Execute
TaskList
List all tasks with their current status.
No input required.
Returns: Table of all tasks with ID, status, description, and timestamps.
Permission: ReadOnly
TaskStop
Stop a running task.
Prop
Type
Permission: Execute
TaskOutput
Retrieve the full output of a completed task.
Prop
Type
Permission: ReadOnly
Usage Pattern
The model uses these tools to track multi-step work:
Agent receives: "Refactor the auth module and update the tests"
1. TaskCreate { description: "Refactor auth module" } → abc12345
2. TaskCreate { description: "Update auth tests" } → def67890
3. TaskUpdate { id: "abc12345", status: "running" }
4. ... (reads files, makes edits) ...
5. TaskUpdate { id: "abc12345", status: "completed", output: "Refactored 3 files" }
6. TaskUpdate { id: "def67890", status: "running" }
7. ... (updates tests) ...
8. TaskUpdate { id: "def67890", status: "completed", output: "Updated 5 test files" }Tasks are in-memory only — they don't persist across agent restarts. For durable work tracking, use the session transcript and memory system.
Programmatic Access
The task registry is accessible directly from Rust — you don't have to go through the tool system:
use cersei_tools::tasks::{get_task, list_tasks, clear_tasks, TaskStatus};
// Check a specific task
if let Some(task) = get_task("abc12345") {
println!("Task '{}': {:?}", task.description, task.status);
if task.status == TaskStatus::Completed {
println!("Output: {}", task.output.unwrap_or_default());
}
}
// List all tasks
for task in list_tasks() {
println!("[{}] {:?} — {}", task.id, task.status, task.description);
}
// Clear all tasks (e.g., on session reset)
clear_tasks();This is useful for building custom orchestration on top of the task system — monitoring dashboards, polling loops, or integration with external job runners.
Cron Scheduling
Schedule recurring prompts that execute on a timer. Useful for periodic checks, monitoring, and automated maintenance.
CronEntry
Prop
Type
Tools
CronCreate
Prop
Type
Returns: "Cron job 'abc12345' created: */5 * * * * → Check for new issues"
Permission: Execute
CronList
List all scheduled jobs with run counts and last execution times.
No input required. Permission: ReadOnly
CronDelete
Prop
Type
Permission: Execute
Schedule Format
| Format | Meaning |
|---|---|
*/5 * * * * | Every 5 minutes |
0 * * * * | Every hour |
0 0 * * * | Daily at midnight |
once:30s | One-shot, 30 seconds from now |
once:5m | One-shot, 5 minutes from now |
Cron jobs are in-memory. They run within the current agent session and stop when the agent exits. For persistent scheduling, use external cron or the RemoteTrigger tool.
Programmatic Access
use cersei_tools::cron::{list_crons, clear_crons};
// List all scheduled jobs
for job in list_crons() {
println!(
"[{}] {} → '{}' (ran {} times, last: {:?})",
job.id, job.schedule, job.prompt, job.run_count, job.last_run
);
}
// Clear all cron jobs (e.g., on session end)
clear_crons();Git Worktree Isolation
Create isolated git worktrees for parallel branch work. The agent can work in a separate branch without affecting the main working directory.
EnterWorktree
Prop
Type
Creates a new git worktree with git worktree add -b {branch} {path}. The agent's working directory switches to the worktree.
Permission: Write
ExitWorktree
Prop
Type
Removes the worktree with git worktree remove --force {path}. Working directory returns to the original.
Permission: Write
Pattern
Agent receives: "Try two approaches to fixing the bug"
1. EnterWorktree { branch: "fix-approach-a" }
2. ... (implements approach A) ...
3. Bash { command: "cargo test" }
4. ExitWorktree { path: "/tmp/cersei-wt-fix-approach-a" }
5. EnterWorktree { branch: "fix-approach-b" }
6. ... (implements approach B) ...
7. Bash { command: "cargo test" }
8. ExitWorktree { path: "/tmp/cersei-wt-fix-approach-b" }
9. Report which approach passed testsIntegration Patterns
Tasks + Sessions
Tasks are scoped to the current session's memory. When you resume a session, the in-memory registry is empty — but the task creation and completion events are preserved in the session JSONL transcript. The model can read its own history to understand what was done previously.
use cersei_tools::tasks::{get_task, list_tasks, TaskStatus};
// After agent completes, inspect what it did
let tasks = list_tasks();
let completed = tasks.iter().filter(|t| t.status == TaskStatus::Completed).count();
let failed = tasks.iter().filter(|t| t.status == TaskStatus::Failed).count();
println!("{} tasks completed, {} failed", completed, failed);
for task in &tasks {
if task.status == TaskStatus::Failed {
println!("FAILED: {} — {}", task.description, task.output.as_deref().unwrap_or("no output"));
}
}Monitoring Agent Work
Use the event system to observe task activity in real time:
let agent = Agent::builder()
.provider(Anthropic::from_env()?)
.tools(cersei::tools::all())
.on_event(|e| match e {
AgentEvent::ToolStart { name, input, .. } if name == "TaskCreate" => {
let desc = input["description"].as_str().unwrap_or("?");
eprintln!("[task] creating: {desc}");
}
AgentEvent::ToolStart { name, input, .. } if name == "TaskUpdate" => {
let id = input["id"].as_str().unwrap_or("?");
let status = input["status"].as_str().unwrap_or("?");
eprintln!("[task] {id} → {status}");
}
_ => {}
})
.build()?;Cron + Memory
A scheduled agent that periodically scans the project and updates memory:
use cersei::prelude::*;
use std::time::Duration;
// Run an agent every hour to scan for changes
loop {
let output = Agent::builder()
.provider(Anthropic::from_env()?)
.tools(cersei::tools::coding())
.system_prompt("You maintain project memory. Scan for new files and patterns.")
.max_turns(5)
.permission_policy(AllowReadOnly)
.run_with("Check src/ for new files since last scan. Update memory if needed.")
.await?;
println!("Scan done: {} turns, {} tool calls", output.turns, output.tool_calls.len());
tokio::time::sleep(Duration::from_secs(3600)).await;
}Sleep + Polling
Use the event stream to implement a deploy-and-poll pattern:
let agent = Agent::builder()
.provider(OpenAi::from_env()?)
.tools(cersei::tools::coding())
.permission_policy(AllowAll)
.run_with(
"Run ./deploy.sh, then wait 10 seconds and check the health endpoint at \
http://localhost:8080/health. Retry up to 3 times if it's not ready."
)
.await?;The model uses BashTool for the deploy, SleepTool for the wait (up to 60s per call), and BashTool again for the curl check — all within the standard agentic loop.