clawdesk-runtime
Durable agent runtime providing checkpoint/resume capabilities for long-running agent tasks. Persists agent state so tasks can survive process restarts and be resumed from the last checkpoint.
Dependencies
Internal: clawdesk-types, clawdesk-domain, clawdesk-storage
External: tokio, serde, tracing, thiserror, chrono, uuid
Modules
| Module | Description |
|---|---|
checkpoint | Checkpoint creation and serialization |
executor | Durable task executor with checkpoint support |
resume | Resume logic — restores state from checkpoint |
state | Runtime state management |
Key Types
/// A checkpoint capturing agent state at a point in time
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Checkpoint {
pub id: String,
pub task_id: String,
pub state: serde_json::Value,
pub step: usize,
pub created_at: chrono::DateTime<chrono::Utc>,
pub metadata: HashMap<String, serde_json::Value>,
}
/// Durable task definition
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DurableTask {
pub id: String,
pub name: String,
pub status: TaskStatus,
pub current_step: usize,
pub total_steps: Option<usize>,
pub last_checkpoint: Option<Checkpoint>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum TaskStatus {
Pending,
Running,
Paused,
Completed,
Failed(String),
}
/// Durable task executor
pub struct DurableExecutor {
store: Arc<dyn SessionStore>,
checkpoint_interval: std::time::Duration,
}
impl DurableExecutor {
/// Execute a task with automatic checkpointing
pub async fn execute<F, Fut>(
&self,
task: &mut DurableTask,
step_fn: F,
) -> Result<(), RuntimeError>
where
F: Fn(usize, &serde_json::Value) -> Fut,
Fut: std::future::Future<Output = Result<serde_json::Value, RuntimeError>>,
{
// Execute steps with periodic checkpoints
}
/// Resume a task from its last checkpoint
pub async fn resume(&self, task_id: &str) -> Result<DurableTask, RuntimeError> {
// Load checkpoint and resume execution
}
}
Example Usage
use clawdesk_runtime::{DurableExecutor, DurableTask, TaskStatus};
let executor = DurableExecutor::new(store, Duration::from_secs(30));
let mut task = DurableTask {
id: uuid::Uuid::new_v4().to_string(),
name: "research-task".into(),
status: TaskStatus::Pending,
current_step: 0,
total_steps: Some(5),
last_checkpoint: None,
};
// Execute with checkpointing
executor.execute(&mut task, |step, state| async move {
match step {
0 => search_web(state).await,
1 => analyze_results(state).await,
2 => generate_summary(state).await,
_ => Ok(state.clone()),
}
}).await?;
// Resume after restart
let resumed = executor.resume(&task.id).await?;
tip
Set checkpoint intervals based on the cost of re-execution. More frequent checkpoints use more storage but reduce re-work on failure.