aion-server 0.31.0

Aion workflow server library: HTTP, gRPC, WebSocket, and worker endpoints. Run it with the `aion` binary from the aion-cli crate.
Documentation
//! Requirement (c)'s doc pin: the assistant tool and its credential are on the
//! documented surface, in the words the server actually states.
//!
//! Documentation is the only surface an operator reads before they run
//! anything. A route that hands a subprocess a tool for reading the console
//! screen, under a credential nobody outside this codebase has heard of, is a
//! capability that has to be discoverable — and a doc that described it in words
//! the server does not use would be a second, drifting description.
//!
//! So this reads the documents at COMPILE time and asserts against the server's
//! own constants, never against restated prose: a renamed tool or a rewritten
//! credential sentence fails here rather than leaving the docs quietly wrong.

use aion_integration_acp::catalogue;
use aion_server::assistant::mcp::{
    ASSISTANT_CONTEXT_TOOL, ASSISTANT_MCP_PATH, SESSION_TOKEN_DESCRIPTION, SESSION_TOKEN_KIND,
};
use aion_server::namespace::grants::GRANT_WORDS;

/// The operator API reference.
const API_DOC: &str = include_str!("../../../docs/operations/API.md");

/// The assistant-session document an operator reads before running one.
const SESSIONS_DOC: &str = include_str!("../../../docs/assistant/DIRECT-SESSIONS.md");

/// The shipped development configuration, which is also a worked example of
/// what the `[assistant]` section may say.
const DEV_CONFIG: &str = include_str!("../../../dev-config.toml");

/// Every key the round-2 amendment retired, in the ASSIGNMENT form an example
/// would carry.
///
/// The assignment form, not the bare word: `default_harness` is still a field of
/// the descriptor (this caller's last pick), so a document naming it is fine —
/// what must not appear is `default_harness = `, which is somebody being taught
/// to write a file this server refuses.
const RETIRED_KEYS: &[&str] = &[
    "default_harness = ",
    "spawn_timeout_ms = ",
    "turn_timeout_ms = ",
    "event_buffer = ",
    "tool_confinement = ",
    "exit_grace_ms = ",
    "[assistant.tools]",
];

/// The sentence a document has to carry before it may name a retired key.
const RETIREMENT_SENTENCE: &str = "Retired, not defaulted";

/// Assert `claim` is present in `document`, naming both when it is not.
fn carries(document: &str, name: &str, claim: &str) {
    assert!(
        document.contains(claim),
        "{name} does not carry `{claim}`; a capability an operator cannot read about is one they \
         cannot consent to"
    );
}

/// `assistant_context` and its route are named in `API.md`.
#[test]
fn the_api_reference_names_the_assistant_tool_and_its_route() {
    carries(API_DOC, "docs/operations/API.md", ASSISTANT_CONTEXT_TOOL);
    carries(API_DOC, "docs/operations/API.md", ASSISTANT_MCP_PATH);
    // And it says the separation out loud, because "not in the general
    // catalogue" is the property that keeps one session's screen private.
    carries(
        API_DOC,
        "docs/operations/API.md",
        "not** in the general catalogue",
    );
}

/// The session-token KIND and the two words the requirement names are in
/// `API.md`, and the kind is the server's own constant rather than a spelling
/// the doc invented.
#[test]
fn the_api_reference_names_the_session_token_kind_and_says_what_it_is() {
    carries(API_DOC, "docs/operations/API.md", SESSION_TOKEN_KIND);
    carries(API_DOC, "docs/operations/API.md", "server-minted");
    carries(API_DOC, "docs/operations/API.md", "session-scoped");
    // The doc's description of the credential must be the server's OWN sentence,
    // not a paraphrase — one description of one credential.
    carries(API_DOC, "docs/operations/API.md", SESSION_TOKEN_DESCRIPTION);
}

/// Every grant word this deployment defines is in `API.md`, walked from the
/// vocabulary rather than from a list somebody kept up by hand.
#[test]
fn the_api_reference_names_every_grant_word_with_its_knobs() {
    // Vacuity control: an emptied vocabulary would satisfy the loop below.
    assert!(
        !GRANT_WORDS.is_empty(),
        "the grant vocabulary is empty, so this pin measures nothing"
    );
    for grant in GRANT_WORDS {
        carries(API_DOC, "docs/operations/API.md", grant.word());
        carries(API_DOC, "docs/operations/API.md", grant.header());
    }
}

/// The assistant document carries the grant word, the development header, the
/// session-token kind and the tool — the four things an operator needs in order
/// to know what opening a session actually hands out.
///
/// It is pinned HERE rather than on the CLI's help because there is no
/// `aion assistant` verb: the assistant is opened from the console, so the
/// document an operator reads before running one is the surface that has to
/// carry this.
#[test]
fn the_assistant_document_carries_the_grant_word_and_the_session_token() {
    let name = "docs/assistant/DIRECT-SESSIONS.md";
    carries(SESSIONS_DOC, name, "assistant.sessions");
    carries(SESSIONS_DOC, name, "x-aion-assistant-sessions");
    carries(SESSIONS_DOC, name, SESSION_TOKEN_KIND);
    carries(SESSIONS_DOC, name, "server-minted and session-scoped");
    carries(SESSIONS_DOC, name, ASSISTANT_CONTEXT_TOOL);
    carries(SESSIONS_DOC, name, ASSISTANT_MCP_PATH);
}

/// The negative control for every cell above: the documents really are being
/// read, and a claim that is NOT in them fails.
///
/// Without this, an `include_str!` that resolved to an empty file — or a
/// `contains` over the wrong document — would make every assertion above pass by
/// finding nothing to disagree with.
#[test]
fn the_documents_are_really_being_read() {
    assert!(
        API_DOC.len() > 1_000 && SESSIONS_DOC.len() > 1_000,
        "the documents this pin reads are empty or truncated, so every claim above is vacuous"
    );
    assert!(
        !API_DOC.contains("assistant_screenshot"),
        "the control claim is present, so this pin cannot tell absence from presence"
    );
}

/// O8: the catalogue, its availability, and the last-pick memory are on the
/// documented surface — in `API.md`, in the assistant document, and in the
/// shipped configuration an operator copies from.
///
/// Walked from the CATALOGUE itself rather than from a list kept by hand: a
/// harness this build can launch and no document names is one an operator can
/// only discover by reading the source.
#[test]
fn the_documents_carry_the_catalogue_its_availability_and_the_last_pick() {
    assert!(
        catalogue::CATALOGUE.len() >= 4,
        "the catalogue is empty or short, so the loop below measures nothing"
    );
    for entry in catalogue::CATALOGUE {
        carries(API_DOC, "docs/operations/API.md", entry.id);
        carries(API_DOC, "docs/operations/API.md", &entry.launch());
        carries(
            SESSIONS_DOC,
            "docs/assistant/DIRECT-SESSIONS.md",
            &entry.launch(),
        );
    }
    for (document, name) in [
        (API_DOC, "docs/operations/API.md"),
        (SESSIONS_DOC, "docs/assistant/DIRECT-SESSIONS.md"),
    ] {
        // Availability is a VENUE reading, and saying so is the difference
        // between an operator installing Node.js and an operator filing a bug.
        carries(document, name, "install_hint");
        carries(document, name, "never cached");
        // The last-pick memory: whose it is, and what `null` means.
        carries(document, name, "last pick");
    }
}

/// O8: the documented shape of `[assistant]` has shrunk to the optional account
/// names, and no document teaches a retired knob as though it still worked.
///
/// A retired key may be NAMED — the documents have to say it is gone — but only
/// after the sentence that retires it. A key that appeared earlier would be one
/// an operator copies out of an example into a file the server refuses.
#[test]
fn no_document_still_teaches_a_retired_assistant_knob() {
    for (document, name) in [
        (SESSIONS_DOC, "docs/assistant/DIRECT-SESSIONS.md"),
        (DEV_CONFIG, "dev-config.toml"),
    ] {
        let found = document.find(RETIREMENT_SENTENCE);
        assert!(
            found.is_some(),
            "{name} must say `{RETIREMENT_SENTENCE}` before it names a retired key"
        );
        let retirement = found.unwrap_or_default();
        for key in RETIRED_KEYS {
            if let Some(mentioned) = document.find(key) {
                assert!(
                    mentioned > retirement,
                    "{name} names the retired key `{key}` before it says the key is retired, so \
                     an operator reading top-down copies it into a file this server refuses"
                );
            }
        }
        // The shape that IS taught: a catalogue id, and account names on both
        // sides of `env`.
        carries(document, name, "[[assistant.harness.account]]");
        carries(document, name, "AION_CLAUDE_WORK_DIR");
        carries(document, name, "claude-code");
    }
    // And the one thing a stock server must never be told it needs.
    carries(
        SESSIONS_DOC,
        "docs/assistant/DIRECT-SESSIONS.md",
        "It works out of the box",
    );
}

/// The negative control for the two cells above.
#[test]
fn the_round_two_documents_are_really_being_read() {
    assert!(
        SESSIONS_DOC.len() > 1_000 && DEV_CONFIG.len() > 1_000,
        "the documents these pins read are empty or truncated"
    );
    assert!(
        !SESSIONS_DOC.contains("npx @agentclientprotocol/nonexistent-acp"),
        "the control claim is present, so these pins cannot tell absence from presence"
    );
}