agent-runtime-kernel 0.1.1

Internal model, context, and Tool execution Kernel for Agent Runtime
Documentation
use runtime_kernel_api::KernelFailure;
use runtime_ports::{ModelContent, ModelMessage};
use serde::{Deserialize, Serialize};

const CHECKPOINT_PREFIX: &str = "Runtime execution checkpoint. This is the authoritative minimal state compiled from earlier execution. Continue from NEXT without repeating completed work.\n";

#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub(crate) struct ExecutionCheckpoint {
    #[serde(default = "checkpoint_version")]
    version: u32,
    goal: CheckpointGoal,
    #[serde(default)]
    done: Vec<CheckpointDone>,
    state: CheckpointState,
    #[serde(default)]
    watch: Vec<CheckpointWatch>,
    #[serde(default)]
    next: Vec<CheckpointNext>,
}

#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
struct CheckpointGoal {
    objective: String,
    #[serde(default)]
    acceptance: Vec<String>,
}

#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
struct CheckpointDone {
    action: String,
    #[serde(default)]
    evidence: Vec<String>,
}

#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
struct CheckpointState {
    phase: CheckpointPhase,
    summary: String,
}

#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
enum CheckpointPhase {
    Investigating,
    Diagnosed,
    Implementing,
    ChangedUnverified,
    Verified,
    Blocked,
    Complete,
}

#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
struct CheckpointWatch {
    kind: String,
    content: String,
}

#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
struct CheckpointNext {
    action: String,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    expected_result: Option<String>,
}

pub(crate) struct CompiledCheckpoint {
    pub(crate) message: ModelMessage,
    pub(crate) used_fallback: bool,
}

pub(crate) fn compile_checkpoint(
    candidate: Option<&str>,
    messages: &[ModelMessage],
    current_user_prompt: &str,
    max_chars: usize,
) -> Result<CompiledCheckpoint, KernelFailure> {
    let (checkpoint, used_fallback) = match candidate {
        Some(candidate) => match parse_and_validate(candidate) {
            Ok(checkpoint) => (checkpoint, false),
            Err(error) => (
                fallback_checkpoint(messages, current_user_prompt, &error.message),
                true,
            ),
        },
        None => (
            fallback_checkpoint(
                messages,
                current_user_prompt,
                "no Agent checkpoint compiler produced a candidate",
            ),
            true,
        ),
    };
    let checkpoint = fit_checkpoint(checkpoint, max_chars.max(1_024));
    validate_checkpoint(&checkpoint)?;
    let json = serde_json::to_string(&checkpoint).map_err(|error| {
        KernelFailure::new("CHECKPOINT_SERIALIZATION_FAILED", error.to_string())
    })?;
    Ok(CompiledCheckpoint {
        message: ModelMessage::text(
            runtime_ports::ModelRole::User,
            format!("{CHECKPOINT_PREFIX}{json}"),
        ),
        used_fallback,
    })
}

fn parse_and_validate(candidate: &str) -> Result<ExecutionCheckpoint, KernelFailure> {
    let checkpoint = serde_json::from_str(candidate)
        .map_err(|error| KernelFailure::new("CHECKPOINT_CANDIDATE_INVALID", error.to_string()))?;
    validate_checkpoint(&checkpoint)?;
    Ok(checkpoint)
}

fn validate_checkpoint(checkpoint: &ExecutionCheckpoint) -> Result<(), KernelFailure> {
    if checkpoint.version != checkpoint_version() || checkpoint.goal.objective.trim().is_empty() {
        return Err(KernelFailure::new(
            "CHECKPOINT_INVALID",
            "checkpoint requires version 1 and a non-empty goal.objective",
        ));
    }
    if checkpoint.state.summary.trim().is_empty() {
        return Err(KernelFailure::new(
            "CHECKPOINT_INVALID",
            "checkpoint requires a non-empty state.summary",
        ));
    }
    if !matches!(
        checkpoint.state.phase,
        CheckpointPhase::Blocked | CheckpointPhase::Complete
    ) && !checkpoint
        .next
        .iter()
        .any(|next| !next.action.trim().is_empty())
    {
        return Err(KernelFailure::new(
            "CHECKPOINT_INVALID",
            "an active checkpoint requires at least one next action",
        ));
    }
    Ok(())
}

fn fallback_checkpoint(
    messages: &[ModelMessage],
    current_user_prompt: &str,
    reason: &str,
) -> ExecutionCheckpoint {
    let done = messages
        .iter()
        .flat_map(|message| &message.content)
        .filter_map(|content| match content {
            ModelContent::ToolResult {
                name,
                content,
                failed: false,
                ..
            } => Some(CheckpointDone {
                action: format!("Tool {name} completed"),
                evidence: vec![head_tail(content, 600)],
            }),
            _ => None,
        })
        .rev()
        .take(12)
        .collect::<Vec<_>>();
    let had_tools = messages.iter().any(|message| {
        message.content.iter().any(|content| {
            matches!(
                content,
                ModelContent::ToolUse { .. } | ModelContent::ToolResult { .. }
            )
        })
    });
    ExecutionCheckpoint {
        version: checkpoint_version(),
        goal: CheckpointGoal {
            objective: truncate(current_user_prompt, 1_500),
            acceptance: Vec::new(),
        },
        done,
        state: CheckpointState {
            phase: if had_tools {
                CheckpointPhase::ChangedUnverified
            } else {
                CheckpointPhase::Investigating
            },
            summary: "A deterministic fallback preserved recent execution evidence; current state must be re-verified."
                .into(),
        },
        watch: vec![CheckpointWatch {
            kind: "uncertainty".into(),
            content: format!("Checkpoint compiler fallback: {}", truncate(reason, 500)),
        }],
        next: vec![CheckpointNext {
            action: "Re-verify current state, then continue with the smallest concrete action."
                .into(),
            expected_result: Some("New evidence grounded in the current execution state.".into()),
        }],
    }
}

fn fit_checkpoint(mut checkpoint: ExecutionCheckpoint, max_chars: usize) -> ExecutionCheckpoint {
    checkpoint.version = checkpoint_version();
    checkpoint.goal.objective = truncate(&checkpoint.goal.objective, 1_500);
    checkpoint.goal.acceptance.truncate(8);
    checkpoint.done.truncate(16);
    checkpoint.watch.truncate(12);
    checkpoint.next.truncate(5);
    while checkpoint_chars(&checkpoint) > max_chars {
        if checkpoint.done.len() > 1 {
            checkpoint.done.pop();
        } else if checkpoint.watch.len() > 1 {
            checkpoint.watch.pop();
        } else if checkpoint.goal.acceptance.len() > 1 {
            checkpoint.goal.acceptance.pop();
        } else if checkpoint.next.len() > 1 {
            checkpoint.next.pop();
        } else {
            checkpoint.goal.objective = truncate(&checkpoint.goal.objective, 500);
            checkpoint.state.summary = truncate(&checkpoint.state.summary, 240);
            if let Some(done) = checkpoint.done.first_mut() {
                done.action = truncate(&done.action, 160);
                done.evidence.truncate(1);
                if let Some(evidence) = done.evidence.first_mut() {
                    *evidence = head_tail(evidence, 240);
                }
            }
            if let Some(watch) = checkpoint.watch.first_mut() {
                watch.content = truncate(&watch.content, 180);
            }
            if let Some(next) = checkpoint.next.first_mut() {
                next.action = truncate(&next.action, 240);
                next.expected_result = None;
            }
            break;
        }
    }
    checkpoint
}

fn checkpoint_chars(checkpoint: &ExecutionCheckpoint) -> usize {
    serde_json::to_string(checkpoint)
        .map(|value| value.chars().count())
        .unwrap_or(usize::MAX)
}

fn truncate(value: &str, max_chars: usize) -> String {
    value.trim().chars().take(max_chars).collect()
}

fn head_tail(value: &str, max_chars: usize) -> String {
    let count = value.chars().count();
    if count <= max_chars {
        return value.to_string();
    }
    let separator = "\n...[middle omitted]...\n";
    let remaining = max_chars.saturating_sub(separator.chars().count());
    let head = remaining / 3;
    let tail = remaining.saturating_sub(head);
    format!(
        "{}{}{}",
        value.chars().take(head).collect::<String>(),
        separator,
        value
            .chars()
            .skip(count.saturating_sub(tail))
            .collect::<String>()
    )
}

const fn checkpoint_version() -> u32 {
    1
}

#[cfg(test)]
mod tests {
    use runtime_ports::{ModelContent, ModelRole};

    use super::*;

    #[test]
    fn active_checkpoint_requires_next() {
        let invalid = r#"{"version":1,"goal":{"objective":"task"},"done":[],"state":{"phase":"implementing","summary":"editing"},"watch":[],"next":[]}"#;
        assert_eq!(
            parse_and_validate(invalid).unwrap_err().code,
            "CHECKPOINT_INVALID"
        );
    }

    #[test]
    fn invalid_candidate_falls_back_to_unverified_recent_evidence() {
        let messages = vec![ModelMessage {
            role: ModelRole::Tool,
            content: vec![ModelContent::ToolResult {
                tool_use_id: "one".into(),
                name: "write".into(),
                content: "changed file.rs".into(),
                failed: false,
            }],
        }];
        let compiled = compile_checkpoint(Some("not-json"), &messages, "task", 4_000).unwrap();
        let encoded = serde_json::to_string(&compiled.message).unwrap();
        assert!(compiled.used_fallback);
        assert!(encoded.contains("changed_unverified"));
        assert!(encoded.contains("changed file.rs"));
        assert!(!encoded.contains("\"phase\":\"verified\""));
    }
}