use std::{collections::BTreeSet, sync::Arc};
use async_trait::async_trait;
use runtime_extension_api::AgentPackage;
use runtime_ports::{
CommitDisposition, ExecutionSessions, OperationContext, ResolvedExecutionContext, TokenUsage,
};
use runtime_types::{ConversationId, ExecutionId};
use thiserror::Error;
#[derive(Debug, Clone)]
pub struct KernelLimits {
pub max_model_turns: usize,
pub max_tool_calls: usize,
pub context: ContextLimits,
pub max_tool_output_bytes: usize,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ContextLimits {
pub max_messages: usize,
pub max_input_tokens: u64,
pub reserved_output_tokens: u32,
pub protocol_overhead_tokens: u64,
pub auto_compact: bool,
pub compaction: ContextCompactionLimits,
}
impl Default for ContextLimits {
fn default() -> Self {
Self {
max_messages: 128,
max_input_tokens: 128 * 1024,
reserved_output_tokens: 8 * 1024,
protocol_overhead_tokens: 256,
auto_compact: true,
compaction: ContextCompactionLimits::default(),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ContextCompactionLimits {
pub trigger_percent: u8,
pub target_percent: u8,
pub emergency_percent: u8,
pub min_reclaim_percent: u8,
pub min_turns_between: usize,
pub checkpoint_max_chars: usize,
}
impl Default for ContextCompactionLimits {
fn default() -> Self {
Self {
trigger_percent: 82,
target_percent: 60,
emergency_percent: 95,
min_reclaim_percent: 10,
min_turns_between: 4,
checkpoint_max_chars: 8_000,
}
}
}
#[derive(Clone)]
pub struct KernelSpec {
pub execution_id: ExecutionId,
pub conversation_id: Option<ConversationId>,
pub user_prompt: String,
pub model: String,
pub package: Arc<AgentPackage>,
pub granted_capabilities: BTreeSet<String>,
pub limits: KernelLimits,
}
#[derive(Debug, Clone)]
pub enum KernelEvent {
Started,
ModelStarted {
turn: usize,
},
ModelCompleted {
turn: usize,
},
ToolStarted {
call_id: String,
name: String,
},
ToolCompleted {
call_id: String,
name: String,
failed: bool,
},
Warning {
code: String,
message: String,
},
}
#[async_trait]
pub trait KernelEventSink: Send + Sync {
async fn emit(&self, event: KernelEvent) -> Result<(), KernelFailure>;
}
#[derive(Debug, Clone)]
pub struct KernelOutcome {
pub answer: String,
pub model_turns: usize,
pub tool_calls: usize,
pub usage: TokenUsage,
}
#[derive(Debug, Error, Clone)]
#[error("{code}: {message}")]
pub struct KernelFailure {
pub code: String,
pub message: String,
pub retryable: bool,
pub commit: CommitDisposition,
}
impl KernelFailure {
pub fn new(code: impl Into<String>, message: impl Into<String>) -> Self {
Self {
code: code.into(),
message: message.into(),
retryable: false,
commit: CommitDisposition::NotCommitted,
}
}
}
#[async_trait]
pub trait RuntimeKernel: Send + Sync {
async fn execute(
&self,
operation: OperationContext,
spec: KernelSpec,
sessions: ExecutionSessions,
events: Arc<dyn KernelEventSink>,
) -> Result<KernelOutcome, KernelFailure>;
}
#[async_trait]
pub trait AgentPackageResolver: Send + Sync {
async fn resolve(
&self,
operation: &OperationContext,
resolved: &ResolvedExecutionContext,
) -> Result<Arc<AgentPackage>, KernelFailure>;
}