deepstrike-sdk 0.2.37

DeepStrike Rust SDK — agent framework built on deepstrike-core
Documentation
use deepstrike_core::runtime::repair::{
    reconstruct_messages_with_fallback, repair_events, repair_events_with_cap,
    repair_llm_completed, sanitize_recovery_text, sanitize_recovery_text_bounded,
};
use deepstrike_core::runtime::session::SessionEvent;
use deepstrike_core::types::message::{Content, ContentPart, Message, Role};

use super::session_log::SessionEntry;

pub fn repair_entries(entries: &[SessionEntry]) -> Vec<SessionEntry> {
    repair_entries_with_cap(entries, 0)
}

pub fn repair_entries_with_cap(entries: &[SessionEntry], max_bytes: usize) -> Vec<SessionEntry> {
    let events: Vec<SessionEvent> = entries.iter().map(|e| e.event.clone()).collect();
    repair_events_with_cap(events, max_bytes)
        .into_iter()
        .zip(entries.iter())
        .map(|(event, entry)| SessionEntry {
            seq: entry.seq,
            event,
        })
        .collect()
}

pub fn is_mid_run(entries: &[SessionEntry]) -> bool {
    !entries.is_empty()
        && !entries
            .iter()
            .any(|e| matches!(e.event, SessionEvent::RunTerminal { .. }))
}

pub fn replay_messages(entries: &[SessionEntry]) -> Vec<Message> {
    replay_messages_with_cap(entries, 0)
}

pub fn replay_messages_with_cap(entries: &[SessionEntry], max_bytes: usize) -> Vec<Message> {
    replay_messages_with_cap_and_loader(entries, max_bytes, |_| {
        Err(
            deepstrike_core::context::fault::ContextFault::MissingArchive {
                session_id: String::new(),
                seq: 0,
            },
        )
    })
}

pub fn replay_messages_with_cap_and_loader<F>(
    entries: &[SessionEntry],
    max_bytes: usize,
    load_archive: F,
) -> Vec<Message>
where
    F: FnMut(&str) -> Result<Vec<Message>, deepstrike_core::context::fault::ContextFault>,
{
    let events: Vec<SessionEvent> = entries.iter().map(|e| e.event.clone()).collect();
    reconstruct_messages_with_fallback(&events, "", max_bytes, load_archive)
}

#[cfg(test)]
mod tests {
    use super::*;
    use deepstrike_core::runtime::session::SessionEvent;
    use deepstrike_core::types::message::{Content, ToolCall, ToolResult};

    #[test]
    fn is_mid_run_when_no_terminal() {
        let entries = vec![SessionEntry {
            seq: 0,
            event: SessionEvent::RunStarted {
                run_id: "r1".into(),
                goal: "hi".into(),
                criteria: vec![],
                agent_id: None,
                system_prompt: None,
            },
        }];
        assert!(is_mid_run(&entries));
    }

    #[test]
    fn replay_includes_user_and_assistant() {
        let entries = vec![
            SessionEntry {
                seq: 0,
                event: SessionEvent::RunStarted {
                    run_id: "r1".into(),
                    goal: "ping".into(),
                    criteria: vec![],
                    agent_id: None,
                    system_prompt: None,
                },
            },
            SessionEntry {
                seq: 1,
                event: SessionEvent::LlmCompleted {
                    turn: 0,
                    message: Message {
                        role: Role::Assistant,
                        content: Content::Text("pong".into()),
                        tool_calls: vec![],
                        token_count: None,
                    },
                    provider_replay: None,
                },
            },
        ];
        let msgs = replay_messages(&entries);
        assert_eq!(msgs.len(), 2);
        assert_eq!(msgs[0].role, Role::User);
        assert_eq!(msgs[1].role, Role::Assistant);
    }

    #[test]
    fn replay_tool_completed() {
        let call_id = compact_str::CompactString::new("c1");
        let entries = vec![SessionEntry {
            seq: 0,
            event: SessionEvent::ToolCompleted {
                turn: 0,
                results: vec![ToolResult {
                    call_id: call_id.clone(),
                    output: Content::Text("ok".into()),
                    is_error: false,
                    is_fatal: false,
                    error_kind: None,
                    token_count: None,
                }],
            },
        }];
        let msgs = replay_messages(&entries);
        assert_eq!(msgs.len(), 1);
        assert_eq!(msgs[0].role, Role::Tool);
    }
}