kcode-kennedy-sessions 0.1.0

Kennedy logical session lifecycle and agent orchestration
Documentation
use kcode_kweb_context::{Connection, Context, LoadReport, Node};
use kcode_kweb_db::{Node as KwebNode, NodeId, Owner};
use serde_json::{Value, json};

use super::Service;

pub(crate) fn load_durable_batch(
    api: &Service,
    context: &mut Context,
    durable_ids: &[String],
) -> anyhow::Result<Value> {
    let mut preview = context.clone();
    let mut reports = Vec::with_capacity(durable_ids.len());
    for durable_id in durable_ids {
        reports.push(load_one(api, &mut preview, durable_id)?);
    }
    *context = preview;
    Ok(json!({"loads":reports}))
}

fn load_one(api: &Service, context: &mut Context, durable_id: &str) -> anyhow::Result<LoadReport> {
    let requested = api.kmap_node(durable_id)?;
    let fixed_ids = requested.data.fixed_connections.clone();
    let requested = node_from_kweb(api, requested)?;
    anyhow::ensure!(
        requested.id == durable_id,
        "Kweb returned node {} when {durable_id} was requested",
        requested.id
    );
    let fixed = fixed_ids
        .into_iter()
        .filter(|id| id.to_string() != durable_id)
        .map(|id| api.kmap_node(&id.to_string()))
        .map(|node| node.map_err(anyhow::Error::new))
        .map(|node| node.and_then(|node| node_from_kweb(api, node)))
        .collect::<anyhow::Result<Vec<_>>>()?;
    context
        .apply_load(requested, fixed)
        .map_err(anyhow::Error::new)
}

fn node_from_kweb(api: &Service, node: KwebNode) -> anyhow::Result<Node> {
    let fixed_connections = connections(api, &node.data.fixed_connections)?;
    let recent_connections = connections(api, &node.data.recent_connections)?;
    let owner = match node.data.owner {
        Owner::Unowned => "unowned".into(),
        Owner::SelfNode => node.id.to_string(),
        Owner::Node(id) => id.to_string(),
    };
    Ok(Node {
        id: node.id.to_string(),
        short_name: node.data.short_name,
        short_description: node.data.short_description,
        long_description: node.data.long_description,
        owner,
        fixed_connections,
        recent_connections,
        objects: node.data.objects.iter().map(ToString::to_string).collect(),
        last_modified_by: node.last_author,
        last_modified_at: Some(node.committed_at.to_rfc3339()),
    })
}

fn connections(api: &Service, ids: &[NodeId]) -> anyhow::Result<Vec<Connection>> {
    ids.iter()
        .map(|id| api.kmap_node(&id.to_string()))
        .map(|node| node.map_err(anyhow::Error::new))
        .map(|node| {
            node.map(|node| Connection {
                id: node.id.to_string(),
                short_name: node.data.short_name,
                short_description: node.data.short_description,
            })
        })
        .collect()
}

pub(crate) fn node_from_value(value: &Value) -> anyhow::Result<Node> {
    Node::from_kweb_value(value).map_err(anyhow::Error::new)
}