tiny-agent 0.2.0

一个小而完整的 Rust LLM Agent 运行时:可中断、可恢复、可观测、可插拔的 agent loop / A small but complete LLM agent runtime in Rust — an interruptible, resumable, observable, pluggable agent loop.
Documentation
use crate::sandbox::Sandbox;
use crate::tools::{ToolCtx, ToolOutcome};
use schemars::JsonSchema;
use serde::Deserialize;
use serde_json::json;
use std::sync::Arc;
use std::time::{Duration, Instant};

/// check 的等待窗口:有新输出 / 任务结束就提前返回,否则最多等这么久(避免模型空转狂查)。
const WAIT_WINDOW: Duration = Duration::from_secs(2);
const POLL_INTERVAL: Duration = Duration::from_millis(100);

#[derive(Deserialize, JsonSchema)]
pub struct CheckArgs {
    /// bash 返回的后台任务 id(形如 `task_7`)
    pub id: String,
    /// 可选等待秒数。传了之后 check 会先等待这么多秒,再读取新增输出与状态。
    pub wait_secs: Option<u64>,
}

pub async fn check(
    _sandbox: Arc<dyn Sandbox>,
    args: CheckArgs,
    ctx: ToolCtx,
) -> Result<ToolOutcome, String> {
    if let Some(wait_secs) = args.wait_secs {
        tokio::time::sleep(Duration::from_secs(wait_secs)).await;
        let snap = ctx.tasks.poll(&args.id).ok_or_else(|| {
            format!(
                "未找到后台任务: {}(可能已被 kill,或进程重启后丢失)",
                args.id
            )
        })?;
        return snapshot_to_outcome(snap);
    }

    let deadline = Instant::now() + WAIT_WINDOW;
    loop {
        let snap = ctx.tasks.poll(&args.id).ok_or_else(|| {
            format!(
                "未找到后台任务: {}(可能已被 kill,或进程重启后丢失)",
                args.id
            )
        })?;

        if !snap.new_output.is_empty() || snap.finished || Instant::now() >= deadline {
            return snapshot_to_outcome(snap);
        }
        tokio::time::sleep(POLL_INTERVAL).await;
    }
}

fn snapshot_to_outcome(snap: crate::tasks::TaskSnapshot) -> Result<ToolOutcome, String> {
    if let Some(err) = snap.error {
        return Err(err);
    }
    let status = if snap.finished {
        "completed"
    } else {
        "running"
    };
    Ok(json!({
        "status": status,
        "exit_code": snap.exit_code,
        "output": snap.new_output,
    })
    .into())
}