magi-code 0.80.1

Repository-aware CLI coding agent for terminal work
Documentation
//! Source bodies are read once on the bounded registration worker, never by execution.
use super::super::runtime::ServiceRuntime;
use super::*;

#[derive(Deserialize)]
#[serde(deny_unknown_fields)]
struct SourceRegistration {
    #[serde(deserialize_with = "Deserialize::deserialize")]
    expected_revision: Option<String>,
    resource: SourceResource,
}
#[derive(Deserialize)]
#[serde(deny_unknown_fields)]
struct SourceResource {
    kind: ResourceKind,
    name: String,
    description: String,
    source: Source,
}
#[derive(Deserialize)]
#[serde(rename_all = "snake_case")]
enum ResourceKind {
    Tool,
    Skill,
}
#[derive(Deserialize)]
#[serde(tag = "kind", rename_all = "snake_case", deny_unknown_fields)]
enum Source {
    Inline { content: String },
    WorkspaceFile { path: String },
}
#[derive(Deserialize)]
#[serde(deny_unknown_fields)]
struct SourceContract {
    input_schema: Schema,
    output_schema: Schema,
    #[serde(default = "default_timeout")]
    timeout_ms: u64,
}
pub(in crate::service) struct PreparedSource {
    pub payload: Value,
    pub descriptor: Value,
    // Exact source bytes remain memory-only and are charged with the immutable snapshot.
    pub content: Arc<str>,
}

pub(in crate::service) fn prepare_source(
    payload: &Value,
    runtime: &ServiceRuntime,
) -> Result<PreparedSource, Code> {
    let registration: SourceRegistration = decode(payload)?;
    let resource = registration.resource;
    registry::validate_resource_name(&resource.name)?;
    registry::validate_catalog_name(&resource.name, runtime)?;
    if resource.description.len() > 512 || resource.description.contains('\0') {
        return Err(Code::InvalidPayload);
    }
    let (kind, limit) = match resource.kind {
        ResourceKind::Tool => ("tool", 32768),
        ResourceKind::Skill => ("skill", 16384),
    };
    let content = match resource.source {
        Source::Inline { content } => content,
        Source::WorkspaceFile { path } => read_workspace_source(runtime, &path, limit)?,
    };
    if content.len() > limit {
        return Err(Code::LimitExceeded);
    }
    if content.contains('\0') {
        return Err(Code::InvalidPayload);
    }
    let mut revision = Sha256::new();
    revision.update(b"magi-application-resource-v1");
    for bytes in [
        kind.as_bytes(),
        resource.name.as_bytes(),
        resource.description.as_bytes(),
        content.as_bytes(),
    ] {
        revision.update((bytes.len() as u64).to_be_bytes());
        revision.update(bytes);
    }
    let descriptor = json!({"kind":kind,"name":resource.name,"description":resource.description,
        "resource_id":uuid::Uuid::new_v4().to_string(),"revision_id":uuid::Uuid::new_v4().to_string(),
        "content_sha256":crate::hex::lower_hex(Sha256::digest(content.as_bytes())),
        "revision_sha256":crate::hex::lower_hex(revision.finalize()),"captured_bytes":content.len()});
    let body = match resource.kind {
        ResourceKind::Tool => {
            let value = super::super::persistent::parse_unique_json(content.as_bytes())?;
            let source: SourceContract = decode(&value)?;
            let contract = Contract {
                name: resource.name,
                description: resource.description,
                input_schema: source.input_schema,
                output_schema: source.output_schema,
                timeout_ms: source.timeout_ms,
            };
            validate_contract(&contract)?;
            json!({"kind":"tool","contract":contract})
        }
        ResourceKind::Skill => {
            json!({"kind":"skill","name":resource.name,"description":resource.description,"content":content})
        }
    };
    Ok(PreparedSource {
        payload: json!({"expected_revision":registration.expected_revision,"resource":body}),
        descriptor,
        content: Arc::from(content),
    })
}

#[cfg(unix)]
fn read_workspace_source(
    runtime: &ServiceRuntime,
    relative: &str,
    limit: usize,
) -> Result<String, Code> {
    use std::{
        fs::{File, OpenOptions},
        os::{
            fd::{AsRawFd, FromRawFd},
            unix::fs::{MetadataExt, OpenOptionsExt},
        },
        path::Path,
    };
    let invalid = || Code::InvalidPayload;
    if relative.len() > 1024
        || relative
            .split('/')
            .any(|part| part.is_empty() || part == "." || part == "..")
        || Path::new(relative).is_absolute()
        || relative.contains('\0')
    {
        return Err(invalid());
    }
    let path = runtime.cwd.join(relative);
    // Reuse the existing credential/session/checkpoint exclusion policy. The descriptor
    // walk below, not this path check, provides no-follow containment.
    let resolved = path.canonicalize().map_err(|_| invalid())?;
    if !resolved.starts_with(&runtime.cwd)
        || resolved.starts_with(&runtime.config.paths.root)
        || !matches!(
            crate::checkpoints::classify_snapshot_eligibility(
                &resolved,
                &runtime.cwd,
                &runtime.config.paths
            ),
            crate::checkpoints::SnapshotEligibility::Eligible
        )
        || relative.split('/').any(|part| {
            let part = part.to_ascii_lowercase();
            matches!(
                part.as_str(),
                ".git"
                    | ".magi-code"
                    | ".ssh"
                    | ".aws"
                    | ".config"
                    | "auth.json"
                    | "id_rsa"
                    | "id_ed25519"
            ) || part.starts_with(".env")
                || part.ends_with(".pem")
                || part.ends_with(".key")
        })
    {
        return Err(invalid());
    }
    let mut file = OpenOptions::new()
        .read(true)
        .custom_flags(libc::O_DIRECTORY | libc::O_NOFOLLOW | libc::O_CLOEXEC)
        .open("/")
        .map_err(|_| invalid())?;
    let components: Vec<_> = path
        .components()
        .filter_map(|component| match component {
            std::path::Component::Normal(name) => Some(name),
            _ => None,
        })
        .collect();
    for (index, component) in components.iter().enumerate() {
        let name = std::ffi::CString::new(component.as_encoded_bytes()).map_err(|_| invalid())?;
        let directory_flag = if index + 1 == components.len() {
            0
        } else {
            libc::O_DIRECTORY
        };
        // SAFETY: file owns the parent descriptor; name is one NUL-free component.
        let fd = unsafe {
            libc::openat(
                file.as_raw_fd(),
                name.as_ptr(),
                libc::O_RDONLY
                    | libc::O_NOFOLLOW
                    | libc::O_CLOEXEC
                    | libc::O_NONBLOCK
                    | directory_flag,
            )
        };
        if fd < 0 {
            return Err(invalid());
        }
        // SAFETY: openat returned a new owned descriptor.
        file = unsafe { File::from_raw_fd(fd) };
    }
    let before = file.metadata().map_err(|_| invalid())?;
    if !before.is_file() || before.nlink() != 1 {
        return Err(invalid());
    }
    if before.len() > limit as u64 {
        return Err(Code::LimitExceeded);
    }
    let mut bytes = Vec::new();
    (&mut file)
        .take(limit as u64 + 1)
        .read_to_end(&mut bytes)
        .map_err(|_| invalid())?;
    let after = file.metadata().map_err(|_| invalid())?;
    let identity = |metadata: &std::fs::Metadata| {
        (
            metadata.dev(),
            metadata.ino(),
            metadata.nlink(),
            metadata.len(),
            metadata.mtime(),
            metadata.mtime_nsec(),
            metadata.ctime(),
            metadata.ctime_nsec(),
        )
    };
    let current = std::fs::symlink_metadata(&path).map_err(|_| invalid())?;
    if identity(&before) != identity(&after)
        || identity(&after) != identity(&current)
        || !current.is_file()
        || bytes.len() as u64 != after.len()
    {
        return Err(invalid());
    }
    if bytes.len() > limit {
        return Err(Code::LimitExceeded);
    }
    String::from_utf8(bytes).map_err(|_| invalid())
}
#[cfg(not(unix))]
fn read_workspace_source(_: &ServiceRuntime, _: &str, _: usize) -> Result<String, Code> {
    Err(Code::UnsupportedCapability)
}