kaynine-runtime 0.1.0

Runtime actors, durable runs, approval flows, and policy chains for Kaynine
Documentation
# Kaynine

Kaynine is a host-agnostic, single-agent framework for Rust applications. It
provides the agent loop, model adapters, tools, policy enforcement, event
streaming, and durable sessions without coupling your product to a UI stack.

## Features

- OpenAI Responses, Anthropic Messages, and OpenAI-compatible providers
- Streaming responses, tool calls, cancellation, steering, and approval flows
- SQLite-backed session recovery, branching, and context compaction
- Composable prompts, product context, and prompt-only skills

## Core integration

The host owns credentials, tools, policy, and presentation. The following
example creates a durable session, connects an OpenAI-compatible provider, and
streams one run. Subscribe before starting the run so no live event is missed.

Add the facade crate and every Kaynine module is available under one
dependency:

```toml
kaynine = "0.1"
```

```rust
use async_trait::async_trait;
use kaynine::core::{
    budget::BudgetPolicy,
    error::CredentialError,
    event::RealtimeEvent,
    ids::{ModelId, SessionId},
    message::ContentBlock,
    policy::AllowAllPolicy,
    provider::{
        CredentialProvider, CredentialRequest, Credentials, ModelCapabilities,
    },
    store::CreateSessionRequest,
};
use kaynine::providers::openai_compatible::OpenAiCompatibleProvider;
use kaynine::runtime::{AgentRuntime, StartRunRequest, SubscriptionItem};
use kaynine::store::sqlite::SqliteStore;
use std::{path::Path, sync::Arc, time::Duration};

struct EnvCredentials;

#[async_trait]
impl CredentialProvider for EnvCredentials {
    async fn resolve(
        &self,
        _request: CredentialRequest,
    ) -> Result<Credentials, CredentialError> {
        Ok(Credentials {
            bearer: std::env::var("KAYNINE_API_KEY").ok(),
            headers: Vec::new(),
        })
    }
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let store = Arc::new(SqliteStore::open(Path::new("kaynine.db"))?);
    let runtime = AgentRuntime::new(store);
    let session_id = SessionId::generate();
    let model = ModelId::from("your-model");

    runtime
        .create_session(CreateSessionRequest {
            command: None,
            session_id: session_id.clone(),
            definition_id: "my-agent".into(),
            definition_version: 1,
            default_model: model.clone(),
            metadata: serde_json::json!({}),
        })
        .await?;
    let branch_id = runtime.list_branches(&session_id).await?.remove(0).branch_id;
    let mut events = runtime.watch_session(&session_id).await?;

    let capabilities = ModelCapabilities {
        context_tokens: 128_000,
        max_output_tokens: 8_192,
        supports_tools: true,
        supports_images: false,
        supports_reasoning: true,
    };
    let provider = Arc::new(OpenAiCompatibleProvider::new(
        "openai-compatible",
        "http://localhost:8080/v1",
        serde_json::json!({ "your-model": capabilities }),
    )?);

    runtime
        .start_run(StartRunRequest {
            command_id: uuid::Uuid::new_v4().to_string(),
            session_id: session_id.clone(),
            branch_id,
            content: vec![ContentBlock::Text {
                text: "Hello from Kaynine".into(),
            }],
            model_override: None,
            reasoning_override: None,
            capabilities,
            system_prompt: "You are a helpful assistant.".into(),
            provider: provider.clone(),
            token_counter: provider,
            credentials: Arc::new(EnvCredentials),
            tools: Vec::new(),
            budget: BudgetPolicy::default(),
            max_turns: Some(8),
            policy: Arc::new(AllowAllPolicy), // Use a stricter policy for tools.
            approval_timeout: None,
            prompt: None,
            compaction: None,
            compaction_selector: None,
        })
        .await?;

    while let Some(item) = events.next().await {
        if let SubscriptionItem::Event(event) = item {
            match event.payload {
                RealtimeEvent::TextDelta { text, .. } => print!("{text}"),
                RealtimeEvent::RunCompleted | RealtimeEvent::RunCancelled => break,
                RealtimeEvent::RunFailed { reason } => {
                    return Err(std::io::Error::other(format!("{reason:?}")).into());
                }
                _ => {}
            }
        }
    }

    runtime.shutdown(Duration::from_secs(2)).await?;
    Ok(())
}
```

See [`examples/host-demo`](examples/host-demo/src/main.rs) for a headless host
and [`examples/tauri-minimal`](examples/tauri-minimal/README.md) for a Tauri v2
integration.

## Crates

| Crate | Responsibility |
| --- | --- |
| `kaynine` | Facade crate re-exporting everything below as one dependency |
| `kaynine-core` | Domain types, extension traits, and the agent loop |
| `kaynine-runtime` | Sessions, runs, subscriptions, and the host facade |
| `kaynine-providers` | Model provider adapters |
| `kaynine-store` | SQLite persistence |
| `kaynine-tools` | Sandboxed file and process tools |