a3s-code-core 8.0.3

A3S Code Core - Embeddable AI agent library with tool execution
Documentation
use super::catalog::WorkspaceChunkCatalog;
use super::chunk::{chunk_file, ChunkFileRequest};
use super::eligibility::WorkspaceEligibilityPolicy;
use super::types::{ChunkCatalogLimits, ChunkingConfig, WorkspaceIndexError};
use super::LexicalSearchRequest;
use crate::workspace::{LocalWorkspaceFile, LocalWorkspaceFileStatus, WorkspacePath};
use serde::Deserialize;
use std::sync::Arc;

mod chunking_strategies;
mod hybrid_query;
mod lifecycle;
mod rerank;
mod semantic;
mod semantic_batching;
mod semantic_query;

#[test]
fn chunking_is_utf8_safe_deterministic_and_bounded_by_lines_and_bytes() {
    let config = ChunkingConfig {
        max_lines: 2,
        max_bytes: 8,
        max_chunks_per_file: 16,
    };
    let content = "alpha\n工作区\nomega\n";
    let request = || ChunkFileRequest {
        path: "src/lib.rs",
        language: Some("rust"),
        source_revision: 7,
        content,
    };
    let first = chunk_file(request(), config).unwrap();
    let second = chunk_file(request(), config).unwrap();

    assert_eq!(first.content_digest, second.content_digest);
    assert_eq!(first.chunks.len(), second.chunks.len());
    assert!(first.chunks.iter().all(|chunk| chunk.text.len() <= 8));
    assert!(first
        .chunks
        .iter()
        .all(|chunk| content.is_char_boundary(chunk.start_byte)
            && content.is_char_boundary(chunk.end_byte)));
    let rebuilt = first
        .chunks
        .iter()
        .map(|chunk| chunk.text.as_ref())
        .collect::<String>();
    assert_eq!(rebuilt, content);
    assert_eq!(
        first
            .chunks
            .iter()
            .map(|chunk| chunk.id.clone())
            .collect::<Vec<_>>(),
        second
            .chunks
            .iter()
            .map(|chunk| chunk.id.clone())
            .collect::<Vec<_>>()
    );
}

#[test]
fn chunking_rejects_over_limit_input_before_retaining_extra_ranges() {
    let error = chunk_file(
        ChunkFileRequest {
            path: "huge.txt",
            language: None,
            source_revision: 1,
            content: "abcdefghijklmnopqrstuvwxyz",
        },
        ChunkingConfig {
            max_lines: 1,
            max_bytes: 4,
            max_chunks_per_file: 2,
        },
    )
    .unwrap_err();

    assert!(matches!(
        error,
        WorkspaceIndexError::TooManyChunks { limit: 2, .. }
    ));
}

#[test]
fn catalog_budget_failure_preserves_the_published_snapshot() {
    let catalog = WorkspaceChunkCatalog::new(
        ChunkingConfig::default(),
        ChunkCatalogLimits {
            max_files: 1,
            max_chunks: 8,
            max_text_bytes: 12,
            max_index_bytes: 1024 * 1024,
        },
    )
    .unwrap();
    let first_path = WorkspacePath::from_normalized("a.rs");
    let second_path = WorkspacePath::from_normalized("b.rs");
    let first = catalog
        .replace_file(&first_path, Some("rust"), 1, "fn a() {}\n")
        .unwrap();
    let error = catalog
        .replace_file(&second_path, Some("rust"), 2, "fn b() {}\n")
        .unwrap_err();

    assert!(matches!(error, WorkspaceIndexError::BudgetExceeded { .. }));
    let after = catalog.snapshot().unwrap();
    assert_eq!(after.revision(), first.revision());
    assert_eq!(after.paths(), ["a.rs"]);
}

#[test]
fn lexical_index_budget_failure_preserves_the_published_snapshot() {
    let catalog = WorkspaceChunkCatalog::new(
        ChunkingConfig::default(),
        ChunkCatalogLimits {
            max_files: 8,
            max_chunks: 8,
            max_text_bytes: 1024,
            max_index_bytes: 1,
        },
    )
    .unwrap();
    let before = catalog.snapshot().unwrap();
    let error = catalog
        .replace_file(
            &WorkspacePath::from_normalized("src/lib.rs"),
            Some("rust"),
            1,
            "pub fn bounded_index() {}\n",
        )
        .unwrap_err();

    assert!(matches!(
        error,
        WorkspaceIndexError::BudgetExceeded {
            resource: "index byte estimate",
            ..
        }
    ));
    let after = catalog.snapshot().unwrap();
    assert_eq!(after.revision(), before.revision());
    assert_eq!(after.source_revision(), before.source_revision());
    assert!(after.paths().is_empty());
}

#[test]
fn catalog_rejects_root_and_parent_traversal_paths() {
    let catalog =
        WorkspaceChunkCatalog::new(ChunkingConfig::default(), ChunkCatalogLimits::default())
            .unwrap();
    for path in [
        WorkspacePath::root(),
        WorkspacePath::from_normalized("../outside.rs"),
    ] {
        assert!(matches!(
            catalog.replace_file(&path, Some("rust"), 1, "content"),
            Err(WorkspaceIndexError::InvalidConfig(_))
        ));
    }
}

#[test]
fn catalog_queries_hold_immutable_snapshots_during_replacement() {
    let catalog =
        WorkspaceChunkCatalog::new(ChunkingConfig::default(), ChunkCatalogLimits::default())
            .unwrap();
    let path = WorkspacePath::from_normalized("src/cache.rs");
    let old = catalog
        .replace_file(&path, Some("rust"), 1, "session cache invalidation\n")
        .unwrap();
    let old_chunk = Arc::clone(&old.chunks()[0]);
    let new = catalog
        .replace_file(&path, Some("rust"), 2, "credential expiry guard\n")
        .unwrap();

    assert_eq!(
        old.chunks()[0].text.as_ref(),
        "session cache invalidation\n"
    );
    assert!(Arc::ptr_eq(&old_chunk, &old.chunks()[0]));
    assert_eq!(new.chunks()[0].text.as_ref(), "credential expiry guard\n");
    assert_ne!(old.content_digest(&path), new.content_digest(&path));
}

#[test]
fn incremental_lexical_search_preserves_bm25_identifier_and_cjk_behavior() {
    let catalog =
        WorkspaceChunkCatalog::new(ChunkingConfig::default(), ChunkCatalogLimits::default())
            .unwrap();
    catalog
        .replace_file(
            &WorkspacePath::from_normalized("src/path_policy.rs"),
            Some("rust"),
            1,
            "pub struct LocalWorkspaceAccessPolicy;\n",
        )
        .unwrap();
    catalog
        .replace_file(
            &WorkspacePath::from_normalized("src/cache.rs"),
            Some("rust"),
            2,
            "session cache invalidation policy\n",
        )
        .unwrap();
    catalog
        .replace_file(
            &WorkspacePath::from_normalized("src/zh.rs"),
            Some("rust"),
            3,
            "工作区权限策略阻止越界访问\n",
        )
        .unwrap();
    let snapshot = catalog.snapshot().unwrap();

    let identifier = snapshot
        .lexical_search(&LexicalSearchRequest::new("LocalWorkspaceAccessPolicy"))
        .unwrap();
    assert_eq!(identifier.hits[0].chunk.path.as_ref(), "src/path_policy.rs");
    let cjk = snapshot
        .lexical_search(&LexicalSearchRequest::new("工作区权限策略"))
        .unwrap();
    assert_eq!(cjk.hits[0].chunk.path.as_ref(), "src/zh.rs");
    let paraphrase = snapshot
        .lexical_search(&LexicalSearchRequest::new("login token validation"))
        .unwrap();
    assert!(paraphrase.hits.is_empty());
}

#[test]
fn incremental_lexical_index_matches_the_locked_native_bm25_fixture() {
    let fixture: RelevanceFixture = serde_json::from_str(include_str!(concat!(
        env!("CARGO_MANIFEST_DIR"),
        "/tests/fixtures/workspace-retrieval-v1/corpus.json"
    )))
    .unwrap();
    let catalog =
        WorkspaceChunkCatalog::new(ChunkingConfig::default(), ChunkCatalogLimits::default())
            .unwrap();
    for (index, document) in fixture.documents.iter().enumerate() {
        catalog
            .replace_file(
                &WorkspacePath::from_normalized(&document.path),
                document.path.ends_with(".rs").then_some("rust"),
                index as u64 + 1,
                &document.content,
            )
            .unwrap();
    }
    let snapshot = catalog.snapshot().unwrap();

    for query in fixture.queries {
        let result = snapshot
            .lexical_search(&LexicalSearchRequest::new(&query.query))
            .unwrap();
        let paths = result
            .hits
            .iter()
            .map(|hit| hit.chunk.path.to_string())
            .collect::<Vec<_>>();
        assert_eq!(paths, query.expected_bm25_paths, "query {}", query.id);
    }
}

#[test]
fn concurrent_file_replacements_do_not_lose_partitions() {
    let catalog =
        WorkspaceChunkCatalog::new(ChunkingConfig::default(), ChunkCatalogLimits::default())
            .unwrap();
    let threads = (0..16)
        .map(|index| {
            let catalog = Arc::clone(&catalog);
            std::thread::spawn(move || {
                catalog
                    .replace_file(
                        &WorkspacePath::from_normalized(format!("src/file_{index}.rs")),
                        Some("rust"),
                        1,
                        &format!("pub fn file_{index}() {{}}\n"),
                    )
                    .unwrap();
            })
        })
        .collect::<Vec<_>>();
    for thread in threads {
        thread.join().unwrap();
    }

    let snapshot = catalog.snapshot().unwrap();
    assert_eq!(snapshot.file_count(), 16);
    assert_eq!(snapshot.chunk_count(), 16);
}

#[test]
fn eligibility_excludes_sensitive_generated_binary_and_oversized_files() {
    let policy = WorkspaceEligibilityPolicy::default();
    assert!(policy.admits(&manifest_file("src/lib.rs", 20, 1)));
    for path in [
        ".env",
        ".env.production",
        ".a3s/config.acl",
        ".claude/settings.local.json",
        ".codex/session.json",
        ".docker/config.json",
        ".git/config",
        ".npmrc",
        "secrets.json",
        "credentials.toml",
        "keys/server.pem",
    ] {
        assert!(!policy.admits(&manifest_file(path, 20, 1)), "{path}");
    }
    let mut generated = manifest_file("generated.rs", 20, 1);
    generated.generated = true;
    assert!(!policy.admits(&generated));
    let mut binary = manifest_file("blob.bin", 20, 1);
    binary.binary = true;
    assert!(!policy.admits(&binary));
    assert!(!policy.admits(&manifest_file("large.rs", 600 * 1024, 1)));
}

fn manifest_file(path: &str, size: u64, modified_ms: u64) -> LocalWorkspaceFile {
    LocalWorkspaceFile {
        path: path.to_owned(),
        size,
        modified_ms: Some(modified_ms),
        language: path.ends_with(".rs").then(|| "rust".to_owned()),
        status: LocalWorkspaceFileStatus::Tracked,
        binary: false,
        generated: false,
    }
}

#[derive(Clone, Debug, Deserialize)]
struct FixtureDocument {
    path: String,
    content: String,
}

#[derive(Debug, Deserialize)]
struct RelevanceFixture {
    documents: Vec<FixtureDocument>,
    queries: Vec<RelevanceQuery>,
}

#[derive(Debug, Deserialize)]
struct RelevanceQuery {
    id: String,
    query: String,
    expected_bm25_paths: Vec<String>,
}