everruns 0.17.26

Build and run durable AI agents in Rust — the application-facing entrypoint to the Everruns agentic framework
Documentation
//! Downstream-style coverage for the unified Framework capability entrypoint.

use everruns::{
    Agent, BuildError, CapabilityRef, CapabilitySpec, CompactionConfig, FunctionTool,
    IntoCapability, Model, ToolSearch,
};
use serde_json::{Value, json};

fn builder() -> everruns::AgentBuilder {
    Agent::builder()
        .instructions("Use the configured capabilities.")
        .model(Model::simulated("done"))
}

#[tokio::test]
async fn typed_compaction_runs_offline() {
    let agent = builder()
        .capability(CompactionConfig::new().budget_percent(0.85))
        .build()
        .expect("typed compaction config builds");

    let turn = agent
        .session()
        .run("Keep this context useful.")
        .await
        .expect("offline turn runs");
    assert!(turn.success);
}

#[tokio::test]
async fn typed_tool_search_activates_the_model_adaptive_builtin() {
    let agent = builder()
        .capability(
            ToolSearch::automatic()
                .threshold(1)
                .never_defer(["get_current_time"]),
        )
        .capability("current_time")
        .build()
        .expect("typed tool search builds");

    let session = agent.session();
    let context = session.inspect().await.expect("context assembles");
    assert!(context.tools.iter().any(|tool| tool.name == "tool_search"));
    assert!(
        context
            .tools
            .iter()
            .any(|tool| tool.name == "get_current_time")
    );
}

#[tokio::test]
async fn dynamic_reference_activates_a_known_capability() {
    let agent = builder()
        .capability(CapabilityRef::new("current_time").config(json!({})))
        .build()
        .expect("dynamic reference builds");

    let session = agent.session();
    let context = session.inspect().await.expect("context assembles");
    assert!(
        context
            .tools
            .iter()
            .any(|tool| tool.name == "get_current_time")
    );
}

struct ExternalCurrentTime;

impl IntoCapability for ExternalCurrentTime {
    fn into_capability(self) -> CapabilitySpec {
        CapabilityRef::new("current_time").into()
    }
}

#[tokio::test]
async fn external_conversion_contract_needs_only_the_facade() {
    let agent = builder()
        .capability(ExternalCurrentTime)
        .build()
        .expect("external capability value builds");

    let session = agent.session();
    let context = session.inspect().await.expect("context assembles");
    assert!(
        context
            .tools
            .iter()
            .any(|tool| tool.name == "get_current_time")
    );
}

#[tokio::test]
async fn unknown_dynamic_reference_remains_an_open_noop_until_implemented() {
    let agent = builder()
        .capability(
            CapabilityRef::new("vendor.custom").config(json!({ "mode": "database-driven" })),
        )
        .build()
        .expect("open third-party reference builds");

    let turn = agent
        .session()
        .run("Continue without a local implementation.")
        .await
        .expect("unknown references do not break offline execution");
    assert!(turn.success);
}

#[test]
fn invalid_ids_and_json_boundaries_are_rejected() {
    for id in [
        "",
        "2fast",
        "has space",
        "vendor/custom",
        "__everruns_private",
    ] {
        let error = builder()
            .capability(CapabilityRef::new(id))
            .build()
            .expect_err("invalid capability id must fail");
        assert!(matches!(error, BuildError::InvalidCapability { .. }));
    }

    let error = builder()
        .capability(CapabilityRef::new("vendor.custom").config(json!(["not", "an", "object"])))
        .build()
        .expect_err("non-object config must fail");
    assert!(matches!(error, BuildError::InvalidCapability { .. }));
}

#[test]
fn dynamic_config_is_redacted_from_framework_debug_output() {
    let reference = CapabilityRef::new("vendor.custom").config(json!({
        "credential": "must-not-appear"
    }));
    assert!(!format!("{reference:?}").contains("must-not-appear"));

    let agent = builder()
        .capability(reference)
        .build()
        .expect("dynamic reference builds");
    assert!(!format!("{agent:?}").contains("must-not-appear"));
}

#[test]
fn known_builtin_configs_use_the_implementation_validator() {
    let error = builder()
        .capability(CapabilityRef::new("compaction").config(json!({ "budget_percent": 2.0 })))
        .build()
        .expect_err("invalid built-in config must fail at Agent build");
    assert!(matches!(
        error,
        BuildError::InvalidCapability { ref id, .. } if id == "compaction"
    ));

    let error = builder()
        .capability(CapabilityRef::new("auto_tool_search").config(json!({
            "never_defer": ["bad name"]
        })))
        .build()
        .expect_err("invalid typed tool-search field must fail");
    assert!(matches!(
        error,
        BuildError::InvalidCapability { ref id, .. } if id == "auto_tool_search"
    ));
}

#[test]
fn duplicate_ids_across_typed_dynamic_and_string_inputs_are_rejected() {
    let error = builder()
        .capability(CompactionConfig::new())
        .capability(CapabilityRef::new("compaction"))
        .build()
        .expect_err("typed and dynamic refs must collide");
    assert_eq!(
        error,
        BuildError::DuplicateCapability {
            id: "compaction".into(),
        }
    );

    let error = builder()
        .capability(ToolSearch::automatic())
        .capability("auto_tool_search")
        .build()
        .expect_err("typed and string refs must collide");
    assert_eq!(
        error,
        BuildError::DuplicateCapability {
            id: "auto_tool_search".into(),
        }
    );

    let error = builder()
        .capability(CapabilityRef::new("virtual_bash"))
        .capability("bashkit_shell")
        .build()
        .expect_err("a built-in alias and canonical ID must collide");
    assert_eq!(
        error,
        BuildError::DuplicateCapability {
            id: "bashkit_shell".into(),
        }
    );
}

#[test]
fn function_tool_and_capability_reference_do_not_overwrite_each_other() {
    let tool = FunctionTool::new(
        "vendor_lookup",
        "Look up a vendor record.",
        json!({ "type": "object", "properties": {} }),
        |_: Value| async move { Ok::<_, String>(json!({ "found": true })) },
    );
    let error = builder()
        .capability("vendor_lookup")
        .tool(tool)
        .build()
        .expect_err("function implementation and reference must collide");
    assert_eq!(
        error,
        BuildError::DuplicateCapability {
            id: "vendor_lookup".into(),
        }
    );

    let reserved = FunctionTool::new(
        "__everruns_framework_lifecycle_hooks",
        "Attempt to shadow a private implementation.",
        json!({ "type": "object", "properties": {} }),
        |_: Value| async move { Ok::<_, String>(json!({})) },
    );
    let error = builder()
        .tool(reserved)
        .build()
        .expect_err("function implementation must not use a reserved capability id");
    assert!(matches!(
        error,
        BuildError::InvalidCapability { ref id, .. }
            if id == "__everruns_framework_lifecycle_hooks"
    ));
}

#[cfg(feature = "capabilities")]
mod definitions {
    use everruns::{Agent, BuildError, CapabilityRef, Model, capability};

    #[derive(capability::Deserialize, capability::JsonSchema)]
    #[serde(crate = "everruns::capability::serde")]
    #[schemars(crate = "everruns::capability::schemars")]
    struct EchoInput {
        value: String,
    }

    #[derive(capability::Serialize, capability::JsonSchema)]
    #[serde(crate = "everruns::capability::serde")]
    #[schemars(crate = "everruns::capability::schemars")]
    struct EchoOutput {
        value: String,
    }

    struct Echo;

    #[capability::async_trait]
    impl capability::Handler for Echo {
        type Input = EchoInput;
        type Output = EchoOutput;
        type Error = capability::Error;

        fn name(&self) -> &str {
            "vendor_echo"
        }

        fn description(&self) -> &str {
            "Echo one vendor value."
        }

        async fn execute(
            &self,
            input: Self::Input,
            _context: capability::Context,
        ) -> Result<Self::Output, Self::Error> {
            Ok(EchoOutput { value: input.value })
        }
    }

    fn builder() -> everruns::AgentBuilder {
        Agent::builder()
            .instructions("Use vendor tools.")
            .model(Model::simulated("done"))
    }

    fn definition(id: &str) -> capability::Definition {
        capability::Definition::new(id, "Vendor", "Vendor-defined tools.").tool(Echo)
    }

    #[tokio::test]
    async fn code_definition_registers_and_activates_through_capability() {
        let agent = builder()
            .capability(definition("vendor.echo"))
            .build()
            .expect("code definition builds");

        let session = agent.session();
        let context = session.inspect().await.expect("context assembles");
        assert!(context.tools.iter().any(|tool| tool.name == "vendor_echo"));
    }

    #[test]
    fn definition_reference_and_builtin_implementation_collisions_are_rejected() {
        let error = builder()
            .capability(CapabilityRef::new("vendor.echo"))
            .capability(definition("vendor.echo"))
            .build()
            .expect_err("reference and implementation must collide");
        assert_eq!(
            error,
            BuildError::DuplicateCapability {
                id: "vendor.echo".into(),
            }
        );

        let error = builder()
            .capability(definition("current_time"))
            .build()
            .expect_err("code definition must not shadow a built-in");
        assert_eq!(
            error,
            BuildError::DuplicateCapability {
                id: "current_time".into(),
            }
        );
    }
}