kowalski-core 2.3.1

Core library of Kowalski, a Rust multi-agent framework: agents with MCP tools and memory, and hordes (step pipelines with triggers) over Ollama or OpenAI-compatible models
Documentation
//! Minimal linear horde draft for tests and smoke runs.

use crate::horde_graph::HordeEdge;
use crate::rookery::types::{PenguinSpec, RookeryDraft};

/// Three-step linear horde: ingest → process → deliver (generic LLM + ingest).
pub fn minimal_linear_draft() -> RookeryDraft {
    RookeryDraft {
        id: "rookery-demo".into(),
        display_name: "Rookery Demo Horde".into(),
        description: "Minimal born horde for validate and agent-app smoke.".into(),
        capability_prefix: None,
        pipeline: vec![
            "collect".into(),
            "process".into(),
            "deliver".into(),
        ],
        edges: vec![],
        triggers: vec![],
        penguins: vec![
            PenguinSpec {
                name: "collect".into(),
                kind: "ingest".into(),
                display_name: "Collect Agent".into(),
                description: "Captures source input into the workdir.".into(),
                prompt_body: "Ingest stage: source is provided by the operator run input.".into(),
                agent_body: None,
                output: "debug/raw/".into(),
                context_paths: vec![],
                ..Default::default()
            },
            PenguinSpec {
                name: "process".into(),
                kind: "process".into(),
                display_name: "Process Agent".into(),
                description: "Transforms the collected artifact.".into(),
                prompt_body: "You are a **process** stage.\n\nSummarize and structure the attached source. Use clear markdown sections.\n".into(),
                agent_body: None,
                output: "debug/stage-process.md".into(),
                context_paths: vec!["@artifact@".into()],
                ..Default::default()
            },
            PenguinSpec {
                name: "deliver".into(),
                kind: "deliver".into(),
                display_name: "Deliver Agent".into(),
                description: "Produces the final handoff artifact.".into(),
                prompt_body: "You are a **deliver** stage.\n\nProduce a concise final handoff document from the context.\n".into(),
                agent_body: None,
                output: "HANDOFF.md".into(),
                context_paths: vec!["@artifact@".into()],
                ..Default::default()
            },
        ],
        default_question: Some("What is the key takeaway?".into()),
        default_topic: Some("federation".into()),
        workdir: Some("output".into()),
        delivery_title: Some("Handoff".into()),
        delivery_note: None,
        delivery_root_rel: Some("HANDOFF.md".into()),
        ..Default::default()
    }
}

fn edge(from: &str, to: &str) -> HordeEdge {
    HordeEdge {
        from: from.into(),
        to: to.into(),
        when: None,
        max_loops: None,
    }
}

/// Fork/join horde for DAG birth / validate tests.
pub fn minimal_dag_draft() -> RookeryDraft {
    RookeryDraft {
        id: "rookery-dag-demo".into(),
        display_name: "Rookery DAG Demo".into(),
        description: "Fork after ingest, join, then deliver.".into(),
        capability_prefix: None,
        pipeline: vec![
            "ingest".into(),
            "branch-a".into(),
            "branch-b".into(),
            "join".into(),
            "deliver".into(),
        ],
        edges: vec![
            edge("ingest", "branch-a"),
            edge("ingest", "branch-b"),
            edge("branch-a", "join"),
            edge("branch-b", "join"),
            edge("join", "deliver"),
        ],
        triggers: vec![],
        penguins: vec![
            PenguinSpec {
                name: "ingest".into(),
                kind: "ingest".into(),
                display_name: "Ingest".into(),
                description: "Capture source.".into(),
                prompt_body: "Ingest stage.".into(),
                agent_body: None,
                output: "debug/raw/".into(),
                context_paths: vec![],
                ..Default::default()
            },
            PenguinSpec {
                name: "branch-a".into(),
                kind: "process".into(),
                display_name: "Branch A".into(),
                description: "First branch.".into(),
                prompt_body: "Branch A process.".into(),
                agent_body: None,
                output: "debug/stage-branch-a.md".into(),
                context_paths: vec!["@artifact@".into()],
                ..Default::default()
            },
            PenguinSpec {
                name: "branch-b".into(),
                kind: "process".into(),
                display_name: "Branch B".into(),
                description: "Second branch.".into(),
                prompt_body: "Branch B process.".into(),
                agent_body: None,
                output: "debug/stage-branch-b.md".into(),
                context_paths: vec!["@artifact@".into()],
                ..Default::default()
            },
            PenguinSpec {
                name: "join".into(),
                kind: "process".into(),
                display_name: "Join".into(),
                description: "Merge branches.".into(),
                prompt_body: "Join both branches.".into(),
                agent_body: None,
                output: "debug/stage-join.md".into(),
                context_paths: vec![],
                ..Default::default()
            },
            PenguinSpec {
                name: "deliver".into(),
                kind: "deliver".into(),
                display_name: "Deliver".into(),
                description: "Final handoff.".into(),
                prompt_body: "Deliver handoff.".into(),
                agent_body: None,
                output: "HANDOFF.md".into(),
                context_paths: vec!["@artifact@".into()],
                ..Default::default()
            },
        ],
        default_question: Some("What is the outcome?".into()),
        default_topic: Some("federation".into()),
        workdir: Some("output".into()),
        delivery_title: Some("Handoff".into()),
        delivery_note: None,
        delivery_root_rel: Some("HANDOFF.md".into()),
        ..Default::default()
    }
}