agent-sdk-toolkit 0.1.0-alpha.3

Optional tool-pack helpers layered over agent-sdk-core primitive contracts.
Documentation
//! Concrete workspace tool helpers layered over core tool/effect contracts. Use these
//! modules for bounded read, search, edit, write, and format-aware extraction
//! behavior under a host-selected workspace policy. Reads search local files;
//! edit/write helpers may mutate files only through explicit executor calls. This
//! file contains the summary portion of that contract.
//!
use agent_sdk_core::AgentError;

use super::{RenderedRead, add_truncation_guidance, text};
use crate::workspace::{
    read_pipeline::{WorkspaceFileKind, WorkspaceReadDetection, WorkspaceReaderStep},
    util::truncate_bytes,
};

/// Render binary summary.
/// This parses caller-provided bytes into a bounded rendered read response and does not write
/// workspace files.
pub(super) fn render_binary_summary(bytes: &[u8], max_output_bytes: u64) -> RenderedRead {
    let mut rendered = RenderedRead {
        content: String::new(),
        content_summary: Some(format!(
            "Binary file detected ({} bytes). No text reader is available for this file type.",
            bytes.len()
        )),
        truncated: bytes.len() as u64 > max_output_bytes,
        binary: true,
        anchors: Vec::new(),
        reader_pipeline: vec![
            WorkspaceReaderStep::DetectFileType,
            WorkspaceReaderStep::SummarizeBinary,
        ],
        media: None,
        document: None,
        archive: None,
        sqlite: None,
        resource: None,
        warnings: vec!["binary content was not emitted as text".to_string()],
    };
    add_truncation_guidance(&mut rendered);
    rendered
}

/// Renders or detects bounded workspace content for
/// workspace::readers::summary. It may read already-approved local file data
/// but does not mutate the workspace.
pub(super) fn render_bounded_prefix_read(
    bytes: &[u8],
    detection: &WorkspaceReadDetection,
    full_byte_len: u64,
    max_output_bytes: u64,
) -> Result<RenderedRead, AgentError> {
    let guidance = format!(
        "file exceeds workspace max_file_bytes; returned a bounded prefix of {} byte(s) from {} byte(s). Use workspace_search/grep or a narrower range read for more.",
        bytes.len(),
        full_byte_len
    );
    let mut rendered = match detection.kind {
        WorkspaceFileKind::Text | WorkspaceFileKind::Markdown | WorkspaceFileKind::Json => {
            let safe_text = String::from_utf8_lossy(bytes);
            let mut rendered = text::render_text(&safe_text, max_output_bytes, true, false);
            if std::str::from_utf8(bytes).is_err() {
                rendered.warnings.push(
                    "bounded prefix ended inside an invalid UTF-8 sequence; replacement characters may appear at the prefix boundary"
                        .to_string(),
                );
            }
            rendered
        }
        _ => {
            let summary = format!(
                "{:?} file is larger than the workspace full-read cap ({} bytes). Parser adapters that require the whole file were not run.\n{}",
                detection.kind, full_byte_len, guidance
            );
            let truncated = summary.len() as u64 > max_output_bytes;
            RenderedRead {
                content: if truncated {
                    truncate_bytes(&summary, max_output_bytes as usize)
                } else {
                    summary.clone()
                },
                content_summary: Some(summary),
                truncated: true,
                binary: detection.binary,
                anchors: Vec::new(),
                reader_pipeline: vec![
                    WorkspaceReaderStep::DetectFileType,
                    WorkspaceReaderStep::ReadBoundedPrefix,
                    WorkspaceReaderStep::SummarizeBinary,
                ],
                media: None,
                document: None,
                archive: None,
                sqlite: None,
                resource: None,
                warnings: Vec::new(),
            }
        }
    };
    if !rendered
        .reader_pipeline
        .contains(&WorkspaceReaderStep::ReadBoundedPrefix)
    {
        rendered
            .reader_pipeline
            .push(WorkspaceReaderStep::ReadBoundedPrefix);
    }
    rendered.truncated = true;
    add_truncation_guidance(&mut rendered);
    rendered.warnings.push(guidance);
    rendered
        .warnings
        .push("content_hash covers returned prefix bytes, not the full file".to_string());
    Ok(rendered)
}