vv-agent 0.7.0

VectorVein agent runtime, SDK, CLI, tools, and workspace backends
Documentation
use serde_json::Value;

use crate::types::{Message, MessageRole};

use super::text::extract_between;

pub(super) fn collect_original_user_messages(messages: &[Message]) -> Vec<String> {
    let mut collected = Vec::new();
    for message in messages.iter().skip(1) {
        if message.role != MessageRole::User {
            continue;
        }
        let content = message.content.trim();
        if content.is_empty() {
            continue;
        }
        if let Some(original) = extract_original_user_request(content) {
            push_unique_original(&mut collected, original.to_string());
        }
        let compressed_originals = extract_compressed_original_user_messages(content);
        for original in compressed_originals {
            push_unique_original(&mut collected, original);
        }
        if content.contains("<Compressed Agent Memory>") {
            continue;
        }
        if extract_original_user_request(content).is_none() {
            push_unique_original(&mut collected, content.to_string());
        }
    }
    collected
}

fn extract_original_user_request(content: &str) -> Option<&str> {
    let start = content.find("<Original User Request>")?;
    let rest = &content[start + "<Original User Request>".len()..];
    let end = rest.find("</Original User Request>")?;
    Some(rest[..end].trim())
}

fn extract_compressed_original_user_messages(content: &str) -> Vec<String> {
    let Some(summary_block) = extract_between(
        content,
        "<Compressed Agent Memory>",
        "</Compressed Agent Memory>",
    ) else {
        return Vec::new();
    };
    extract_first_json_object(summary_block)
        .and_then(|value| {
            value
                .get("original_user_messages")
                .and_then(Value::as_array)
                .map(|items| {
                    items
                        .iter()
                        .filter_map(Value::as_str)
                        .map(str::trim)
                        .filter(|item| !item.is_empty())
                        .map(str::to_string)
                        .collect::<Vec<_>>()
                })
        })
        .unwrap_or_default()
}

fn extract_first_json_object(raw: &str) -> Option<Value> {
    raw.char_indices()
        .filter(|(_, character)| *character == '{')
        .find_map(|(index, _)| {
            serde_json::Deserializer::from_str(&raw[index..])
                .into_iter::<Value>()
                .next()
                .and_then(Result::ok)
                .filter(Value::is_object)
        })
}

fn push_unique_original(collected: &mut Vec<String>, original: String) {
    if !original.is_empty() && !collected.iter().any(|known| known == &original) {
        collected.push(original);
    }
}