claw-core 0.1.2

Embedded local database engine for ClawDB — an agent-native cognitive database
Documentation
//! Tool output store.
//!
//! The `tool_output` table records the results produced by agent tool calls.
//! Each record captures the tool name, the raw output payload, and metadata
//! needed to correlate the output with the originating session and agent.

use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use sqlx::SqlitePool;
use uuid::Uuid;

use crate::error::{ClawError, ClawResult};

/// A single tool-output record.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ToolOutputRecord {
    /// Unique row identifier.
    pub id: Uuid,
    /// The session in which the tool was invoked.
    pub session_id: String,
    /// The name of the tool that produced this output.
    pub tool_name: String,
    /// Serialized JSON output payload.
    pub output: serde_json::Value,
    /// Whether the tool call succeeded.
    pub success: bool,
    /// Timestamp when the tool output was recorded.
    pub created_at: DateTime<Utc>,
}

/// Data-access object for the `tool_output` table.
#[derive(Debug)]
pub struct ToolOutputStore<'a> {
    pool: &'a SqlitePool,
}

impl<'a> ToolOutputStore<'a> {
    /// Create a new store bound to `pool`.
    pub fn new(pool: &'a SqlitePool) -> Self {
        ToolOutputStore { pool }
    }

    /// Insert a new tool-output record.
    ///
    /// # Errors
    ///
    /// Returns a [`ClawError`] if the SQL execution fails.
    pub async fn insert(&self, record: &ToolOutputRecord) -> ClawResult<()> {
        sqlx::query(
            r#"
            INSERT INTO tool_output (id, session_id, tool_name, output, success, created_at)
            VALUES (?, ?, ?, ?, ?, ?)
            "#,
        )
        .bind(record.id.to_string())
        .bind(&record.session_id)
        .bind(&record.tool_name)
        .bind(serde_json::to_string(&record.output)?)
        .bind(record.success)
        .bind(record.created_at.to_rfc3339())
        .execute(self.pool)
        .await?;

        Ok(())
    }

    /// Fetch all tool-output records for a given `session_id`, ordered by
    /// `created_at` ascending.
    ///
    /// # Errors
    ///
    /// Returns a [`ClawError`] if the query fails.
    pub async fn get_by_session(&self, session_id: &str) -> ClawResult<Vec<ToolOutputRecord>> {
        let rows = sqlx::query_as::<_, (String, String, String, String, bool, String)>(
            "SELECT id, session_id, tool_name, output, success, created_at \
             FROM tool_output WHERE session_id = ? ORDER BY created_at ASC",
        )
        .bind(session_id)
        .fetch_all(self.pool)
        .await?;

        rows.into_iter()
            .map(|(id, session_id, tool_name, output, success, created_at)| {
                Ok(ToolOutputRecord {
                    id: Uuid::parse_str(&id).map_err(|e| ClawError::Store(e.to_string()))?,
                    session_id,
                    tool_name,
                    output: serde_json::from_str(&output)?,
                    success,
                    created_at: DateTime::parse_from_rfc3339(&created_at)
                        .map_err(|e| ClawError::Store(e.to_string()))?
                        .with_timezone(&Utc),
                })
            })
            .collect()
    }
}