vv-agent 0.6.2

VectorVein agent runtime, SDK, CLI, tools, and workspace backends
Documentation
use std::path::{Path, PathBuf};

use super::ToolResultArtifactConfig;

pub(super) fn persist_tool_content(
    content: &str,
    tool_call_id: Option<&str>,
    config: &ToolResultArtifactConfig,
    cycle_index: Option<u32>,
) -> Option<String> {
    let workspace = config.workspace.as_ref()?;
    let artifact_path = build_tool_artifact_path(tool_call_id, &config.artifact_dir, cycle_index);
    let target = workspace.join(&artifact_path);
    if let Some(parent) = target.parent() {
        std::fs::create_dir_all(parent).ok()?;
    }
    std::fs::write(&target, content).ok()?;
    Some(path_to_string(&artifact_path))
}

fn build_tool_artifact_path(
    tool_call_id: Option<&str>,
    artifact_dir: &Path,
    cycle_index: Option<u32>,
) -> PathBuf {
    let safe_id = sanitize_tool_call_id(tool_call_id.unwrap_or("tool_result"));
    let filename = format!("{safe_id}.txt");
    match cycle_index {
        Some(cycle_index) => artifact_dir
            .join(format!("cycle_{cycle_index}"))
            .join(filename),
        None => artifact_dir.join(filename),
    }
}

fn sanitize_tool_call_id(value: &str) -> String {
    let safe = value
        .chars()
        .map(|ch| {
            if ch.is_ascii_alphanumeric() || matches!(ch, '.' | '_' | '-') {
                ch
            } else {
                '_'
            }
        })
        .collect::<String>();
    if safe.trim().is_empty() {
        "tool_result".to_string()
    } else {
        safe
    }
}

fn path_to_string(path: &Path) -> String {
    path.to_string_lossy().replace('\\', "/")
}