Skip to main content

codetether_agent/cli/
run_checkpoint.rs

1//! `codetether run` checkpoint/resume orchestration helpers.
2
3use crate::session::{RunCheckpoint, Session, auto_resume_prompt};
4use anyhow::Result;
5use std::path::{Path, PathBuf};
6
7pub struct RunResumePlan {
8    pub prompt: String,
9    pub remaining: usize,
10}
11
12pub fn validate_auto_continue(limit: Option<usize>) -> Result<()> {
13    if matches!(limit, Some(0)) {
14        anyhow::bail!("--auto-continue-until must be at least 1");
15    }
16    Ok(())
17}
18
19pub fn should_checkpoint(message_count_before: usize, session: &Session, max_steps: usize) -> bool {
20    session.messages.len() > message_count_before && max_steps > 0
21}
22
23/// Persist a checkpoint when the step budget is exhausted.
24///
25/// Extracts real browser URL, completed actions, blockers, and next intended
26/// action from the session message history.
27pub async fn persist_exhaustion_checkpoint(
28    session: &mut Session,
29    objective: &str,
30    max_steps: usize,
31    workspace: &Path,
32) -> Result<PathBuf> {
33    let cp = RunCheckpoint::from_session_messages(
34        objective,
35        max_steps,
36        session.id.clone(),
37        Some(workspace.to_path_buf()),
38        session.messages.len(),
39        &session.messages,
40    );
41    session.save_run_checkpoint(cp).await
42}
43
44pub async fn resume_plan(session: &Session, remaining: usize) -> Result<Option<RunResumePlan>> {
45    let Some(checkpoint) = session.load_run_checkpoint().await? else {
46        return Ok(None);
47    };
48    Ok(Some(RunResumePlan {
49        prompt: auto_resume_prompt(&checkpoint),
50        remaining,
51    }))
52}