agent-kernel 0.1.0

Minimal Agent orchestration kernel for multi-agent discussion
Documentation
//! Simple discussion example using a `MockRuntime`.
//!
//! Demonstrates the minimal setup needed to run a multi-agent discussion:
//! 1. Implement `AgentRuntime` with canned responses.
//! 2. Create two `Agent`s.
//! 3. Call `discuss()` for 3 rounds and print every `AgentEvent`.
//!
//! Run with:
//! ```bash
//! cargo run --example simple_discussion
//! ```

use agent_kernel::{Agent, AgentEvent, AgentRuntime, BoxFuture, Message, discuss};
use tokio::sync::mpsc;
use tokio_util::sync::CancellationToken;

// ── MockRuntime ───────────────────────────────────────────────────────────────

/// A test runtime that returns a canned response for each agent.
///
/// In production you would replace this with an HTTP call to an LLM provider.
struct MockRuntime;

impl AgentRuntime for MockRuntime {
    fn respond<'a>(
        &'a self,
        agent: &'a Agent,
        history: &'a [Message],
    ) -> BoxFuture<'a, anyhow::Result<String>> {
        Box::pin(async move {
            // Echo the agent name and the number of messages seen so far.
            Ok(format!(
                "[{}] I've seen {} messages. My take: this is interesting.",
                agent.name,
                history.len()
            ))
        })
    }
}

// ── main ─────────────────────────────────────────────────────────────────────

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let runtime = MockRuntime;

    // Two agents — agents[0] is the primary (produces the final summary).
    let agents = vec![
        Agent {
            name: "alice".to_string(),
            soul_md: "You are Alice, a systems architect. Be concise.".to_string(),
            model: "anthropic/claude-sonnet-4-6".to_string(),
        },
        Agent {
            name: "bob".to_string(),
            soul_md: "You are Bob, a security engineer. Challenge assumptions.".to_string(),
            model: "anthropic/claude-sonnet-4-6".to_string(),
        },
    ];

    // Channel for observability events.
    let (tx, mut rx) = mpsc::channel::<AgentEvent>(64);
    let cancel = CancellationToken::new();

    // Spawn a task to print events as they arrive.
    let printer = tokio::spawn(async move {
        while let Some(event) = rx.recv().await {
            match &event {
                AgentEvent::Progress { current_round, max_rounds } => {
                    println!("--- Round {current_round}/{max_rounds} ---");
                }
                AgentEvent::Round { round, agent_name, content } => {
                    println!("  [{round}] {agent_name}: {content}");
                }
                AgentEvent::Summary { content } => {
                    println!("\nSummary: {content}");
                }
                AgentEvent::Completed => {
                    println!("\nDiscussion completed.");
                }
                AgentEvent::Converged { reason } => {
                    println!("\nConverged: {reason}");
                }
                AgentEvent::Cancelled => {
                    println!("\nCancelled.");
                }
                AgentEvent::Evolved { agent, old_version, new_version } => {
                    println!("\n{agent} evolved: v{old_version} -> v{new_version}");
                }
            }
        }
    });

    // Run the discussion — 3 rounds, no cancellation.
    let summary = discuss(
        &runtime,
        &agents,
        "What are the trade-offs between microservices and a monolith?",
        3,
        cancel,
        tx,
    )
    .await?;

    // Wait for the printer to flush all events.
    printer.await?;

    println!("\nFinal summary returned by discuss(): {summary}");

    Ok(())
}