aion-server 0.29.0

Aion workflow server library: HTTP, gRPC, WebSocket, and worker endpoints. Run it with the `aion` binary from the aion-cli crate.
Documentation
//! `assistant_document_check` — the AWL checker over the shared document.
//!
//! The agent's own quality gate: it runs THE editor's check — the same
//! [`check_source_in_workspace`] the console's check surface calls, with the
//! same workspace root, so the agent and the operator can never be answered
//! two different verdicts about one buffer. The text checked is the shared
//! document as the transcript now holds it, edits already folded in — so the
//! loop is edit, check, fix, and only then tell the operator what changed.

use aion_mcp::tools::service::{ToolFailure, ToolOutcome};
use serde_json::{Value, json};

use crate::ServerState;
use crate::awl::{CheckRequest, check_source_in_workspace, workspace};

use super::caller::AssistantSessionCaller;

/// Answer `assistant_document_check` for the session the caller speaks for.
///
/// # Errors
///
/// [`ToolFailure`] when there is no shared document, when the workspace root
/// cannot be resolved, or when the check itself cannot run. A document with
/// diagnostics is NOT a failure — the diagnostics are the answer.
pub(crate) async fn assistant_document_check(
    state: &ServerState,
    caller: &AssistantSessionCaller,
) -> Result<ToolOutcome, ToolFailure> {
    let latest = state
        .assistant_sessions()
        .latest_context(caller.session_id())
        .await
        .map_err(|error| {
            ToolFailure::new(
                format!(
                    "the shared document could not be read for this conversation: {error}. This \
                     does NOT mean nothing is open — the server could not read it."
                ),
                json!({ "code": "context_unreadable" }),
            )
        })?;
    let Some(document) = latest.and_then(|context| context.document) else {
        return Err(ToolFailure::new(
            "there is no document to check: the operator has not shared one from an editor. \
             Call `assistant_context` to see what they have shared."
                .to_owned(),
            json!({ "code": "no_document" }),
        ));
    };
    let root = workspace::workspace_root(state).map_err(|error| {
        ToolFailure::new(
            format!("the workspace root could not be resolved, so the check cannot run: {error}"),
            json!({ "code": "workspace_unresolved" }),
        )
    })?;
    let checked = check_source_in_workspace(
        &root,
        &CheckRequest {
            source: document.text,
            path: Some(document.path.clone()),
        },
    )
    .await
    .map_err(|error| {
        ToolFailure::new(
            format!("the check could not run: {error}"),
            json!({ "code": "check_failed" }),
        )
    })?;
    let diagnostics: Vec<Value> = checked
        .diagnostics
        .iter()
        .map(|diagnostic| {
            json!({
                "line": diagnostic.line,
                "column": diagnostic.column,
                "message": diagnostic.message,
            })
        })
        .collect();
    let summary = if checked.deploys_green {
        format!("`{}` checks clean.", document.path)
    } else {
        format!(
            "`{}` has {} diagnostic{} — fix them with `assistant_document_edit` and check again.",
            document.path,
            diagnostics.len(),
            if diagnostics.len() == 1 { "" } else { "s" },
        )
    };
    Ok(ToolOutcome {
        structured: json!({
            "ok": checked.deploys_green,
            "diagnostics": diagnostics,
        }),
        summary,
    })
}