klieo-core 3.15.0

Core traits + runtime for the klieo agent framework.
Documentation
//! Live capture hook for recording LLM I/O for later replay (ADR-048).

use crate::llm::{ChatRequest, ChatResponse};
use std::sync::Arc;

/// Sink the run loop calls once per successful LLM call so a recorder can build
/// a replayable capture. Without a sink the hook is skipped and a run records no
/// LLM I/O — today's behaviour.
///
/// Contract: invoked only on the success path, after post-LLM guardrails pass
/// and before tool dispatch; never on the error path. Implementations must not
/// fail or block the run — buffer in memory and drain after the run completes.
/// Future methods (e.g. tool-call capture) will carry default impls so existing
/// implementors keep compiling.
pub trait CaptureSink: Send + Sync {
    /// Records one successful LLM call for replay. Fire-and-forget — no failure
    /// may propagate to the run; buffer in memory and drain after it completes.
    fn record_llm_call(&self, request: &ChatRequest, response: &ChatResponse);
}

/// Hands out one [`CaptureSink`] per run.
///
/// A sink buffers calls with no run attribution of its own —
/// `record_llm_call` receives a request and a response, and nothing else — so a
/// sink shared by two runs produces one interleaved buffer that cannot be split
/// again. Whoever drains it then pairs one run's log with another run's calls.
///
/// That is not hypothetical. Sharing one sink across a fan-out shipped in 3.13.0
/// (`AgentContext::child` cloned the parent's sink handle), and the umbrella's
/// own `App` still documents attaching a single App-wide sink to each agent it
/// runs. A provider is the shape that cannot be misused this way: the runtime
/// asks for the sink belonging to a `RunId`, so a child run gets its own by
/// construction.
///
/// Install via [`crate::agent::AgentContextBuilder::capture_sinks`]; every
/// descendant context inherits the provider and mints its own sink from it.
pub trait CaptureSinkProvider: Send + Sync {
    /// The sink for `run`. Called once per context, including for every child
    /// context a fan-out mints, so implementations must return a distinct sink
    /// per distinct `RunId` rather than one shared buffer.
    fn sink_for_run(&self, run: crate::ids::RunId) -> Arc<dyn CaptureSink>;
}