mirror of
https://github.com/ruvnet/RuVector.git
synced 2026-05-24 05:43:58 +00:00
feat(adr-036): AGI cognitive container types + builder
Wire format for packaging the entire AGI framework into a single RVF: Types (rvf-types/src/agi_container.rs): - AgiContainerHeader: 64-byte repr(C) header (RVAG magic) - ContainerSegments: inventory of KERNEL/WASM/VEC/INDEX/WITNESS segments - ExecutionMode: Replay / Verify / Live - 16 TLV tags: model_id, policy, orchestrator, tools, eval, skills, replay_script, kernel_config, coherence, project_instructions, etc. - 12 capability flags: kernel, wasm, orchestrator, world_model, eval, skills, witness, signed, replay, offline, tools, coherence_gates Builder (rvf-runtime/src/agi_container.rs): - AgiContainerBuilder: fluent API for assembling container manifests - ParsedAgiManifest: zero-copy parser with section extraction - HMAC-SHA256 signing for tamper detection - Segment validation per execution mode Container layout: - META segment: AGI manifest (header + TLV config) - KERNEL_SEG: micro Linux kernel (Firecracker vmlinux) - WASM_SEG: interpreter + microkernel modules - VEC_SEG + INDEX_SEG: RuVector world model - WITNESS_SEG: ADR-035 witness chains - CRYPTO_SEG: signing keys and attestation Tests: 13 types + 4 runtime = 17 new tests. All 468 passing. https://claude.ai/code/session_01RnwD4x5cbpB7FPvoyYQz8G
This commit is contained in:
parent
4282461017
commit
a88534bb80
5 changed files with 1060 additions and 0 deletions
|
|
@ -24,3 +24,7 @@ rvf-types = { version = "0.1.0", path = "../rvf-types", features = ["std"] }
|
|||
[dev-dependencies]
|
||||
tempfile = "3"
|
||||
rand = "0.8"
|
||||
|
||||
[[example]]
|
||||
name = "qr_seed_encode"
|
||||
required-features = ["qr"]
|
||||
|
|
|
|||
564
crates/rvf/rvf-runtime/src/agi_container.rs
Normal file
564
crates/rvf/rvf-runtime/src/agi_container.rs
Normal file
|
|
@ -0,0 +1,564 @@
|
|||
//! AGI Cognitive Container builder and validator (ADR-036).
|
||||
//!
|
||||
//! Assembles a complete intelligence runtime into a single RVF artifact:
|
||||
//! micro Linux kernel, Claude Code + Claude Flow configs, world model,
|
||||
//! evaluation harness, witness chains, tool adapters, and policies.
|
||||
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
|
||||
use rvf_types::agi_container::*;
|
||||
|
||||
use crate::seed_crypto;
|
||||
|
||||
/// Builder for assembling an AGI cognitive container manifest.
|
||||
///
|
||||
/// The manifest is a META segment in the RVF file. Other segments
|
||||
/// (KERNEL_SEG, WASM_SEG, VEC_SEG, etc.) are added through the
|
||||
/// main RVF write path; this builder only handles the manifest.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct AgiContainerBuilder {
|
||||
/// Container UUID.
|
||||
pub container_id: [u8; 16],
|
||||
/// Build UUID.
|
||||
pub build_id: [u8; 16],
|
||||
/// Pinned model identifier string.
|
||||
pub model_id: Option<Vec<u8>>,
|
||||
/// Governance policy (binary).
|
||||
pub policy: Option<Vec<u8>>,
|
||||
/// Policy hash from ADR-035 GovernancePolicy.
|
||||
pub policy_hash: [u8; 8],
|
||||
/// Orchestrator config (Claude Code + Claude Flow).
|
||||
pub orchestrator_config: Option<Vec<u8>>,
|
||||
/// MCP tool adapter registry.
|
||||
pub tool_registry: Option<Vec<u8>>,
|
||||
/// Agent role prompts.
|
||||
pub agent_prompts: Option<Vec<u8>>,
|
||||
/// Evaluation task suite.
|
||||
pub eval_tasks: Option<Vec<u8>>,
|
||||
/// Grading rules.
|
||||
pub eval_graders: Option<Vec<u8>>,
|
||||
/// Skill library.
|
||||
pub skill_library: Option<Vec<u8>>,
|
||||
/// Replay script.
|
||||
pub replay_script: Option<Vec<u8>>,
|
||||
/// Kernel boot config.
|
||||
pub kernel_config: Option<Vec<u8>>,
|
||||
/// Network config.
|
||||
pub network_config: Option<Vec<u8>>,
|
||||
/// Coherence gate config.
|
||||
pub coherence_config: Option<Vec<u8>>,
|
||||
/// Project instructions (CLAUDE.md).
|
||||
pub project_instructions: Option<Vec<u8>>,
|
||||
/// Dependency snapshot.
|
||||
pub dependency_snapshot: Option<Vec<u8>>,
|
||||
/// Segment inventory.
|
||||
pub segments: ContainerSegments,
|
||||
/// Extra flags to OR in.
|
||||
pub extra_flags: u16,
|
||||
}
|
||||
|
||||
impl AgiContainerBuilder {
|
||||
/// Create a new builder with container and build IDs.
|
||||
pub fn new(container_id: [u8; 16], build_id: [u8; 16]) -> Self {
|
||||
Self {
|
||||
container_id,
|
||||
build_id,
|
||||
model_id: None,
|
||||
policy: None,
|
||||
policy_hash: [0; 8],
|
||||
orchestrator_config: None,
|
||||
tool_registry: None,
|
||||
agent_prompts: None,
|
||||
eval_tasks: None,
|
||||
eval_graders: None,
|
||||
skill_library: None,
|
||||
replay_script: None,
|
||||
kernel_config: None,
|
||||
network_config: None,
|
||||
coherence_config: None,
|
||||
project_instructions: None,
|
||||
dependency_snapshot: None,
|
||||
segments: ContainerSegments::default(),
|
||||
extra_flags: 0,
|
||||
}
|
||||
}
|
||||
|
||||
/// Pin the model to a specific version.
|
||||
pub fn with_model_id(mut self, model_id: &str) -> Self {
|
||||
self.model_id = Some(model_id.as_bytes().to_vec());
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the governance policy.
|
||||
pub fn with_policy(mut self, policy: &[u8], hash: [u8; 8]) -> Self {
|
||||
self.policy = Some(policy.to_vec());
|
||||
self.policy_hash = hash;
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the Claude Code + Claude Flow orchestrator config.
|
||||
pub fn with_orchestrator(mut self, config: &[u8]) -> Self {
|
||||
self.orchestrator_config = Some(config.to_vec());
|
||||
self.extra_flags |= AGI_HAS_ORCHESTRATOR;
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the MCP tool adapter registry.
|
||||
pub fn with_tool_registry(mut self, registry: &[u8]) -> Self {
|
||||
self.tool_registry = Some(registry.to_vec());
|
||||
self.extra_flags |= AGI_HAS_TOOLS;
|
||||
self
|
||||
}
|
||||
|
||||
/// Set agent role prompts.
|
||||
pub fn with_agent_prompts(mut self, prompts: &[u8]) -> Self {
|
||||
self.agent_prompts = Some(prompts.to_vec());
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the evaluation task suite.
|
||||
pub fn with_eval_tasks(mut self, tasks: &[u8]) -> Self {
|
||||
self.eval_tasks = Some(tasks.to_vec());
|
||||
self.extra_flags |= AGI_HAS_EVAL;
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the grading rules.
|
||||
pub fn with_eval_graders(mut self, graders: &[u8]) -> Self {
|
||||
self.eval_graders = Some(graders.to_vec());
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the promoted skill library.
|
||||
pub fn with_skill_library(mut self, skills: &[u8]) -> Self {
|
||||
self.skill_library = Some(skills.to_vec());
|
||||
self.extra_flags |= AGI_HAS_SKILLS;
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the replay automation script.
|
||||
pub fn with_replay_script(mut self, script: &[u8]) -> Self {
|
||||
self.replay_script = Some(script.to_vec());
|
||||
self.extra_flags |= AGI_REPLAY_CAPABLE;
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the kernel boot configuration.
|
||||
pub fn with_kernel_config(mut self, config: &[u8]) -> Self {
|
||||
self.kernel_config = Some(config.to_vec());
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the network configuration.
|
||||
pub fn with_network_config(mut self, config: &[u8]) -> Self {
|
||||
self.network_config = Some(config.to_vec());
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the coherence gate configuration.
|
||||
pub fn with_coherence_config(mut self, config: &[u8]) -> Self {
|
||||
self.coherence_config = Some(config.to_vec());
|
||||
self.extra_flags |= AGI_HAS_COHERENCE_GATES;
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the project instructions (CLAUDE.md content).
|
||||
pub fn with_project_instructions(mut self, instructions: &[u8]) -> Self {
|
||||
self.project_instructions = Some(instructions.to_vec());
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the dependency snapshot.
|
||||
pub fn with_dependency_snapshot(mut self, snapshot: &[u8]) -> Self {
|
||||
self.dependency_snapshot = Some(snapshot.to_vec());
|
||||
self
|
||||
}
|
||||
|
||||
/// Mark offline capability.
|
||||
pub fn offline_capable(mut self) -> Self {
|
||||
self.extra_flags |= AGI_OFFLINE_CAPABLE;
|
||||
self
|
||||
}
|
||||
|
||||
/// Declare the segment inventory.
|
||||
pub fn with_segments(mut self, segments: ContainerSegments) -> Self {
|
||||
self.segments = segments;
|
||||
self
|
||||
}
|
||||
|
||||
/// Build the manifest TLV payload (sections only, no header).
|
||||
fn build_sections(&self) -> Vec<u8> {
|
||||
let mut payload = Vec::new();
|
||||
|
||||
let mut write_section = |tag: u16, data: &[u8]| {
|
||||
payload.extend_from_slice(&tag.to_le_bytes());
|
||||
payload.extend_from_slice(&(data.len() as u32).to_le_bytes());
|
||||
payload.extend_from_slice(data);
|
||||
};
|
||||
|
||||
write_section(AGI_TAG_CONTAINER_ID, &self.container_id);
|
||||
write_section(AGI_TAG_BUILD_ID, &self.build_id);
|
||||
|
||||
if let Some(ref mid) = self.model_id {
|
||||
write_section(AGI_TAG_MODEL_ID, mid);
|
||||
}
|
||||
if let Some(ref p) = self.policy {
|
||||
write_section(AGI_TAG_POLICY, p);
|
||||
}
|
||||
if let Some(ref oc) = self.orchestrator_config {
|
||||
write_section(AGI_TAG_ORCHESTRATOR, oc);
|
||||
}
|
||||
if let Some(ref tr) = self.tool_registry {
|
||||
write_section(AGI_TAG_TOOL_REGISTRY, tr);
|
||||
}
|
||||
if let Some(ref ap) = self.agent_prompts {
|
||||
write_section(AGI_TAG_AGENT_PROMPTS, ap);
|
||||
}
|
||||
if let Some(ref et) = self.eval_tasks {
|
||||
write_section(AGI_TAG_EVAL_TASKS, et);
|
||||
}
|
||||
if let Some(ref eg) = self.eval_graders {
|
||||
write_section(AGI_TAG_EVAL_GRADERS, eg);
|
||||
}
|
||||
if let Some(ref sl) = self.skill_library {
|
||||
write_section(AGI_TAG_SKILL_LIBRARY, sl);
|
||||
}
|
||||
if let Some(ref rs) = self.replay_script {
|
||||
write_section(AGI_TAG_REPLAY_SCRIPT, rs);
|
||||
}
|
||||
if let Some(ref kc) = self.kernel_config {
|
||||
write_section(AGI_TAG_KERNEL_CONFIG, kc);
|
||||
}
|
||||
if let Some(ref nc) = self.network_config {
|
||||
write_section(AGI_TAG_NETWORK_CONFIG, nc);
|
||||
}
|
||||
if let Some(ref cc) = self.coherence_config {
|
||||
write_section(AGI_TAG_COHERENCE_CONFIG, cc);
|
||||
}
|
||||
if let Some(ref pi) = self.project_instructions {
|
||||
write_section(AGI_TAG_PROJECT_INSTRUCTIONS, pi);
|
||||
}
|
||||
if let Some(ref ds) = self.dependency_snapshot {
|
||||
write_section(AGI_TAG_DEPENDENCY_SNAPSHOT, ds);
|
||||
}
|
||||
|
||||
payload
|
||||
}
|
||||
|
||||
/// Build the manifest: header + TLV sections.
|
||||
pub fn build(self) -> Result<(Vec<u8>, AgiContainerHeader), ContainerError> {
|
||||
let sections = self.build_sections();
|
||||
|
||||
let model_id_hash = match &self.model_id {
|
||||
Some(mid) => seed_crypto::seed_content_hash(mid),
|
||||
None => [0u8; 8],
|
||||
};
|
||||
|
||||
let flags = self.segments.to_flags() | self.extra_flags;
|
||||
|
||||
let created_ns = SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.unwrap_or_default()
|
||||
.as_nanos() as u64;
|
||||
|
||||
let header = AgiContainerHeader {
|
||||
magic: AGI_MAGIC,
|
||||
version: 1,
|
||||
flags,
|
||||
container_id: self.container_id,
|
||||
build_id: self.build_id,
|
||||
created_ns,
|
||||
model_id_hash,
|
||||
policy_hash: self.policy_hash,
|
||||
};
|
||||
|
||||
let mut payload = Vec::with_capacity(AGI_HEADER_SIZE + sections.len());
|
||||
payload.extend_from_slice(&header.to_bytes());
|
||||
payload.extend_from_slice(§ions);
|
||||
|
||||
// Mark manifest as present for validation.
|
||||
let mut segs = self.segments.clone();
|
||||
segs.manifest_present = true;
|
||||
|
||||
Ok((payload, header))
|
||||
}
|
||||
|
||||
/// Build and sign with HMAC-SHA256.
|
||||
pub fn build_and_sign(
|
||||
mut self,
|
||||
key: &[u8],
|
||||
) -> Result<(Vec<u8>, AgiContainerHeader), ContainerError> {
|
||||
self.segments.crypto_present = true;
|
||||
let (unsigned, mut header) = self.build()?;
|
||||
header.flags |= AGI_SIGNED;
|
||||
|
||||
let sig = seed_crypto::sign_seed(key, &unsigned);
|
||||
let mut signed = unsigned;
|
||||
// Re-write the header with SIGNED flag.
|
||||
let header_bytes = header.to_bytes();
|
||||
signed[..AGI_HEADER_SIZE].copy_from_slice(&header_bytes);
|
||||
signed.extend_from_slice(&sig);
|
||||
|
||||
Ok((signed, header))
|
||||
}
|
||||
}
|
||||
|
||||
/// Parsed AGI container manifest with zero-copy section references.
|
||||
#[derive(Debug)]
|
||||
pub struct ParsedAgiManifest<'a> {
|
||||
/// Parsed header.
|
||||
pub header: AgiContainerHeader,
|
||||
/// Model identifier string.
|
||||
pub model_id: Option<&'a [u8]>,
|
||||
/// Policy bytes.
|
||||
pub policy: Option<&'a [u8]>,
|
||||
/// Orchestrator config.
|
||||
pub orchestrator_config: Option<&'a [u8]>,
|
||||
/// Tool registry.
|
||||
pub tool_registry: Option<&'a [u8]>,
|
||||
/// Agent prompts.
|
||||
pub agent_prompts: Option<&'a [u8]>,
|
||||
/// Eval tasks.
|
||||
pub eval_tasks: Option<&'a [u8]>,
|
||||
/// Eval graders.
|
||||
pub eval_graders: Option<&'a [u8]>,
|
||||
/// Skill library.
|
||||
pub skill_library: Option<&'a [u8]>,
|
||||
/// Replay script.
|
||||
pub replay_script: Option<&'a [u8]>,
|
||||
/// Kernel config.
|
||||
pub kernel_config: Option<&'a [u8]>,
|
||||
/// Network config.
|
||||
pub network_config: Option<&'a [u8]>,
|
||||
/// Coherence gate config.
|
||||
pub coherence_config: Option<&'a [u8]>,
|
||||
/// Project instructions.
|
||||
pub project_instructions: Option<&'a [u8]>,
|
||||
/// Dependency snapshot.
|
||||
pub dependency_snapshot: Option<&'a [u8]>,
|
||||
}
|
||||
|
||||
impl<'a> ParsedAgiManifest<'a> {
|
||||
/// Parse a manifest from bytes.
|
||||
pub fn parse(data: &'a [u8]) -> Result<Self, ContainerError> {
|
||||
let header = AgiContainerHeader::from_bytes(data)
|
||||
.map_err(|_| ContainerError::InvalidConfig("invalid header"))?;
|
||||
|
||||
let mut result = Self {
|
||||
header,
|
||||
model_id: None,
|
||||
policy: None,
|
||||
orchestrator_config: None,
|
||||
tool_registry: None,
|
||||
agent_prompts: None,
|
||||
eval_tasks: None,
|
||||
eval_graders: None,
|
||||
skill_library: None,
|
||||
replay_script: None,
|
||||
kernel_config: None,
|
||||
network_config: None,
|
||||
coherence_config: None,
|
||||
project_instructions: None,
|
||||
dependency_snapshot: None,
|
||||
};
|
||||
|
||||
// Parse TLV sections after header.
|
||||
let mut pos = AGI_HEADER_SIZE;
|
||||
while pos + 6 <= data.len() {
|
||||
let tag = u16::from_le_bytes([data[pos], data[pos + 1]]);
|
||||
let length = u32::from_le_bytes([
|
||||
data[pos + 2], data[pos + 3], data[pos + 4], data[pos + 5],
|
||||
]) as usize;
|
||||
pos += 6;
|
||||
|
||||
if pos + length > data.len() {
|
||||
break;
|
||||
}
|
||||
|
||||
let value = &data[pos..pos + length];
|
||||
match tag {
|
||||
AGI_TAG_MODEL_ID => result.model_id = Some(value),
|
||||
AGI_TAG_POLICY => result.policy = Some(value),
|
||||
AGI_TAG_ORCHESTRATOR => result.orchestrator_config = Some(value),
|
||||
AGI_TAG_TOOL_REGISTRY => result.tool_registry = Some(value),
|
||||
AGI_TAG_AGENT_PROMPTS => result.agent_prompts = Some(value),
|
||||
AGI_TAG_EVAL_TASKS => result.eval_tasks = Some(value),
|
||||
AGI_TAG_EVAL_GRADERS => result.eval_graders = Some(value),
|
||||
AGI_TAG_SKILL_LIBRARY => result.skill_library = Some(value),
|
||||
AGI_TAG_REPLAY_SCRIPT => result.replay_script = Some(value),
|
||||
AGI_TAG_KERNEL_CONFIG => result.kernel_config = Some(value),
|
||||
AGI_TAG_NETWORK_CONFIG => result.network_config = Some(value),
|
||||
AGI_TAG_COHERENCE_CONFIG => result.coherence_config = Some(value),
|
||||
AGI_TAG_PROJECT_INSTRUCTIONS => result.project_instructions = Some(value),
|
||||
AGI_TAG_DEPENDENCY_SNAPSHOT => result.dependency_snapshot = Some(value),
|
||||
_ => {} // forward-compat: ignore unknown tags
|
||||
}
|
||||
|
||||
pos += length;
|
||||
}
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
/// Get the model ID as a UTF-8 string.
|
||||
pub fn model_id_str(&self) -> Option<&str> {
|
||||
self.model_id.and_then(|b| core::str::from_utf8(b).ok())
|
||||
}
|
||||
|
||||
/// Check if the manifest has all sections needed for autonomous operation.
|
||||
pub fn is_autonomous_capable(&self) -> bool {
|
||||
self.orchestrator_config.is_some()
|
||||
&& self.eval_tasks.is_some()
|
||||
&& self.eval_graders.is_some()
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Tests
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
const ORCHESTRATOR_CONFIG: &[u8] = br#"{
|
||||
"claude_code": {
|
||||
"model": "claude-opus-4-6",
|
||||
"max_turns": 100,
|
||||
"permission_mode": "bypassPermissions"
|
||||
},
|
||||
"claude_flow": {
|
||||
"topology": "hierarchical",
|
||||
"max_agents": 15,
|
||||
"strategy": "specialized",
|
||||
"memory": "hybrid"
|
||||
}
|
||||
}"#;
|
||||
|
||||
const TOOL_REGISTRY: &[u8] = br#"[
|
||||
{"name": "ruvector_query", "type": "vector_search"},
|
||||
{"name": "ruvector_cypher", "type": "graph_query"},
|
||||
{"name": "ruvector_commit_delta", "type": "write"},
|
||||
{"name": "rvf_snapshot", "type": "snapshot"},
|
||||
{"name": "rvf_witness_export", "type": "export"},
|
||||
{"name": "eval_run", "type": "evaluation"}
|
||||
]"#;
|
||||
|
||||
const COHERENCE_CONFIG: &[u8] = br#"{
|
||||
"min_cut_threshold": 0.7,
|
||||
"contradiction_pressure_max": 0.3,
|
||||
"quarantine_ttl_hours": 24,
|
||||
"skill_promotion_k": 5,
|
||||
"rollback_on_violation": true
|
||||
}"#;
|
||||
|
||||
#[test]
|
||||
fn build_full_container() {
|
||||
let segs = ContainerSegments {
|
||||
kernel_present: true,
|
||||
kernel_size: 5_000_000,
|
||||
wasm_count: 2,
|
||||
wasm_total_size: 60_000,
|
||||
vec_segment_count: 4,
|
||||
index_segment_count: 2,
|
||||
witness_count: 100,
|
||||
crypto_present: false,
|
||||
manifest_present: false,
|
||||
total_size: 0,
|
||||
};
|
||||
|
||||
let builder = AgiContainerBuilder::new([0x01; 16], [0x02; 16])
|
||||
.with_model_id("claude-opus-4-6")
|
||||
.with_policy(b"autonomous", [0xAA; 8])
|
||||
.with_orchestrator(ORCHESTRATOR_CONFIG)
|
||||
.with_tool_registry(TOOL_REGISTRY)
|
||||
.with_agent_prompts(b"You are a coder agent...")
|
||||
.with_eval_tasks(b"[{\"id\":1,\"spec\":\"fix bug\"}]")
|
||||
.with_eval_graders(b"[{\"type\":\"test_pass\"}]")
|
||||
.with_skill_library(b"[]")
|
||||
.with_replay_script(b"#!/bin/sh\nrvf replay $1")
|
||||
.with_kernel_config(b"console=ttyS0 root=/dev/vda")
|
||||
.with_network_config(b"{\"port\":8080}")
|
||||
.with_coherence_config(COHERENCE_CONFIG)
|
||||
.with_project_instructions(b"# CLAUDE.md\nFollow DDD...")
|
||||
.with_dependency_snapshot(b"sha256:abc123")
|
||||
.offline_capable()
|
||||
.with_segments(segs);
|
||||
|
||||
let (payload, header) = builder.build().unwrap();
|
||||
|
||||
assert!(header.is_valid_magic());
|
||||
assert!(header.has_kernel());
|
||||
assert!(header.has_orchestrator());
|
||||
assert!(header.is_replay_capable());
|
||||
assert!(header.is_offline_capable());
|
||||
|
||||
// Parse it back.
|
||||
let parsed = ParsedAgiManifest::parse(&payload).unwrap();
|
||||
assert_eq!(parsed.model_id_str(), Some("claude-opus-4-6"));
|
||||
assert_eq!(parsed.orchestrator_config.unwrap(), ORCHESTRATOR_CONFIG);
|
||||
assert_eq!(parsed.tool_registry.unwrap(), TOOL_REGISTRY);
|
||||
assert_eq!(parsed.coherence_config.unwrap(), COHERENCE_CONFIG);
|
||||
assert!(parsed.project_instructions.is_some());
|
||||
assert!(parsed.is_autonomous_capable());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn signed_container_round_trip() {
|
||||
let key = b"container-signing-key-for-tests!";
|
||||
let builder = AgiContainerBuilder::new([0x10; 16], [0x20; 16])
|
||||
.with_model_id("claude-opus-4-6")
|
||||
.with_orchestrator(ORCHESTRATOR_CONFIG)
|
||||
.with_eval_tasks(b"[]")
|
||||
.with_eval_graders(b"[]")
|
||||
.with_segments(ContainerSegments {
|
||||
kernel_present: true,
|
||||
manifest_present: false,
|
||||
..Default::default()
|
||||
});
|
||||
|
||||
let (payload, header) = builder.build_and_sign(key).unwrap();
|
||||
assert!(header.is_signed());
|
||||
|
||||
// Verify signature.
|
||||
let unsigned_len = payload.len() - 32;
|
||||
let sig = &payload[unsigned_len..];
|
||||
assert!(seed_crypto::verify_seed(key, &payload[..unsigned_len], sig));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn minimal_container() {
|
||||
let builder = AgiContainerBuilder::new([0x30; 16], [0x40; 16])
|
||||
.with_segments(ContainerSegments {
|
||||
kernel_present: true,
|
||||
manifest_present: true,
|
||||
..Default::default()
|
||||
});
|
||||
|
||||
let (payload, header) = builder.build().unwrap();
|
||||
assert!(header.has_kernel());
|
||||
|
||||
let parsed = ParsedAgiManifest::parse(&payload).unwrap();
|
||||
assert!(parsed.model_id.is_none());
|
||||
assert!(!parsed.is_autonomous_capable());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn segment_validation() {
|
||||
// Live mode needs kernel or WASM.
|
||||
let segs = ContainerSegments {
|
||||
manifest_present: true,
|
||||
kernel_present: true,
|
||||
..Default::default()
|
||||
};
|
||||
assert!(segs.validate(ExecutionMode::Live).is_ok());
|
||||
|
||||
// Replay mode needs witness.
|
||||
let segs2 = ContainerSegments {
|
||||
manifest_present: true,
|
||||
witness_count: 50,
|
||||
..Default::default()
|
||||
};
|
||||
assert!(segs2.validate(ExecutionMode::Replay).is_ok());
|
||||
}
|
||||
}
|
||||
|
|
@ -35,6 +35,7 @@ pub mod status;
|
|||
pub mod store;
|
||||
pub mod witness;
|
||||
pub mod write_path;
|
||||
pub mod agi_container;
|
||||
|
||||
pub use adversarial::{
|
||||
adaptive_n_probe, centroid_distance_cv, combined_effective_n_probe,
|
||||
|
|
@ -73,3 +74,6 @@ pub use store::RvfStore;
|
|||
pub use witness::{
|
||||
GovernancePolicy, ParsedWitness, ScorecardBuilder, WitnessBuilder, WitnessError,
|
||||
};
|
||||
pub use agi_container::{
|
||||
AgiContainerBuilder, ParsedAgiManifest,
|
||||
};
|
||||
|
|
|
|||
480
crates/rvf/rvf-types/src/agi_container.rs
Normal file
480
crates/rvf/rvf-types/src/agi_container.rs
Normal file
|
|
@ -0,0 +1,480 @@
|
|||
//! AGI Cognitive Container types (ADR-036).
|
||||
//!
|
||||
//! An AGI container is a single RVF file that packages the complete intelligence
|
||||
//! runtime: micro Linux kernel, Claude Code orchestrator config, Claude Flow
|
||||
//! swarm manager, RuVector world model, evaluation harness, witness chains,
|
||||
//! and tool adapters.
|
||||
//!
|
||||
//! Wire format: 64-byte `AgiContainerHeader` + TLV manifest sections.
|
||||
//! The header is stored as a META segment (SegmentType::Meta) in the RVF file,
|
||||
//! alongside the KERNEL_SEG, WASM_SEG, VEC_SEG, INDEX_SEG, WITNESS_SEG, and
|
||||
//! CRYPTO_SEG that hold the actual payload data.
|
||||
|
||||
/// Magic bytes for AGI container manifest: "RVAG" (RuVector AGI).
|
||||
pub const AGI_MAGIC: u32 = 0x5256_4147;
|
||||
|
||||
/// Size of the AGI container header in bytes.
|
||||
pub const AGI_HEADER_SIZE: usize = 64;
|
||||
|
||||
// --- Flags ---
|
||||
|
||||
/// Container includes a KERNEL_SEG with micro Linux kernel.
|
||||
pub const AGI_HAS_KERNEL: u16 = 1 << 0;
|
||||
/// Container includes WASM_SEG modules.
|
||||
pub const AGI_HAS_WASM: u16 = 1 << 1;
|
||||
/// Container includes Claude Code + Claude Flow orchestrator config.
|
||||
pub const AGI_HAS_ORCHESTRATOR: u16 = 1 << 2;
|
||||
/// Container includes VEC_SEG + INDEX_SEG world model data.
|
||||
pub const AGI_HAS_WORLD_MODEL: u16 = 1 << 3;
|
||||
/// Container includes evaluation harness (task suite + graders).
|
||||
pub const AGI_HAS_EVAL: u16 = 1 << 4;
|
||||
/// Container includes promoted skill library.
|
||||
pub const AGI_HAS_SKILLS: u16 = 1 << 5;
|
||||
/// Container includes ADR-035 witness chain.
|
||||
pub const AGI_HAS_WITNESS: u16 = 1 << 6;
|
||||
/// Container is cryptographically signed (HMAC-SHA256 or Ed25519).
|
||||
pub const AGI_SIGNED: u16 = 1 << 7;
|
||||
/// All tool outputs stored — container supports replay mode.
|
||||
pub const AGI_REPLAY_CAPABLE: u16 = 1 << 8;
|
||||
/// Container can run without network (offline-first).
|
||||
pub const AGI_OFFLINE_CAPABLE: u16 = 1 << 9;
|
||||
/// Container includes MCP tool adapter registry.
|
||||
pub const AGI_HAS_TOOLS: u16 = 1 << 10;
|
||||
/// Container includes coherence gate configuration.
|
||||
pub const AGI_HAS_COHERENCE_GATES: u16 = 1 << 11;
|
||||
|
||||
// --- TLV tags for the manifest payload ---
|
||||
|
||||
/// Container UUID.
|
||||
pub const AGI_TAG_CONTAINER_ID: u16 = 0x0100;
|
||||
/// Build UUID.
|
||||
pub const AGI_TAG_BUILD_ID: u16 = 0x0101;
|
||||
/// Pinned model identifier (UTF-8 string, e.g. "claude-opus-4-6").
|
||||
pub const AGI_TAG_MODEL_ID: u16 = 0x0102;
|
||||
/// Serialized governance policy (binary, per ADR-035).
|
||||
pub const AGI_TAG_POLICY: u16 = 0x0103;
|
||||
/// Claude Code + Claude Flow orchestrator config (JSON or TOML).
|
||||
pub const AGI_TAG_ORCHESTRATOR: u16 = 0x0104;
|
||||
/// MCP tool adapter registry (JSON array of tool schemas).
|
||||
pub const AGI_TAG_TOOL_REGISTRY: u16 = 0x0105;
|
||||
/// Agent role prompts (one per agent type).
|
||||
pub const AGI_TAG_AGENT_PROMPTS: u16 = 0x0106;
|
||||
/// Evaluation task suite (JSON array of task specs).
|
||||
pub const AGI_TAG_EVAL_TASKS: u16 = 0x0107;
|
||||
/// Grading rules (JSON or binary grader config).
|
||||
pub const AGI_TAG_EVAL_GRADERS: u16 = 0x0108;
|
||||
/// Promoted skill library (serialized skill nodes).
|
||||
pub const AGI_TAG_SKILL_LIBRARY: u16 = 0x0109;
|
||||
/// Replay automation script.
|
||||
pub const AGI_TAG_REPLAY_SCRIPT: u16 = 0x010A;
|
||||
/// Kernel boot parameters (command line, initrd config).
|
||||
pub const AGI_TAG_KERNEL_CONFIG: u16 = 0x010B;
|
||||
/// Network configuration (ports, endpoints, TLS).
|
||||
pub const AGI_TAG_NETWORK_CONFIG: u16 = 0x010C;
|
||||
/// Coherence gate thresholds and rules.
|
||||
pub const AGI_TAG_COHERENCE_CONFIG: u16 = 0x010D;
|
||||
/// Claude.md project instructions.
|
||||
pub const AGI_TAG_PROJECT_INSTRUCTIONS: u16 = 0x010E;
|
||||
/// Dependency snapshot hashes (pinned repos, packages).
|
||||
pub const AGI_TAG_DEPENDENCY_SNAPSHOT: u16 = 0x010F;
|
||||
|
||||
// --- Execution mode ---
|
||||
|
||||
/// Container execution mode.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[repr(u8)]
|
||||
pub enum ExecutionMode {
|
||||
/// Replay: no external tool calls, use stored receipts.
|
||||
/// All graders must match exactly. Witness chain must match.
|
||||
Replay = 0,
|
||||
/// Verify: live tool calls, outputs stored and hashed.
|
||||
/// Outputs must pass same tests. Costs within expected bounds.
|
||||
Verify = 1,
|
||||
/// Live: full autonomous operation with governance controls.
|
||||
Live = 2,
|
||||
}
|
||||
|
||||
impl TryFrom<u8> for ExecutionMode {
|
||||
type Error = u8;
|
||||
|
||||
fn try_from(value: u8) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
0 => Ok(Self::Replay),
|
||||
1 => Ok(Self::Verify),
|
||||
2 => Ok(Self::Live),
|
||||
other => Err(other),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Wire-format AGI container header (exactly 64 bytes, `repr(C)`).
|
||||
///
|
||||
/// ```text
|
||||
/// Offset Type Field
|
||||
/// 0x00 u32 magic (0x52564147 "RVAG")
|
||||
/// 0x04 u16 version
|
||||
/// 0x06 u16 flags
|
||||
/// 0x08 [u8; 16] container_id (UUID)
|
||||
/// 0x18 [u8; 16] build_id (UUID)
|
||||
/// 0x28 u64 created_ns (UNIX epoch nanoseconds)
|
||||
/// 0x30 [u8; 8] model_id_hash (SHA-256 truncated)
|
||||
/// 0x38 [u8; 8] policy_hash (SHA-256 truncated)
|
||||
/// ```
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
#[repr(C)]
|
||||
pub struct AgiContainerHeader {
|
||||
/// Magic bytes: AGI_MAGIC.
|
||||
pub magic: u32,
|
||||
/// Format version (currently 1).
|
||||
pub version: u16,
|
||||
/// Bitfield flags indicating which segments are present.
|
||||
pub flags: u16,
|
||||
/// Unique container identifier (UUID).
|
||||
pub container_id: [u8; 16],
|
||||
/// Build identifier (UUID, changes on each repackaging).
|
||||
pub build_id: [u8; 16],
|
||||
/// Creation timestamp (nanoseconds since UNIX epoch).
|
||||
pub created_ns: u64,
|
||||
/// SHA-256 of the pinned model identifier, truncated to 8 bytes.
|
||||
pub model_id_hash: [u8; 8],
|
||||
/// SHA-256 of the governance policy, truncated to 8 bytes.
|
||||
pub policy_hash: [u8; 8],
|
||||
}
|
||||
|
||||
// Compile-time size assertion.
|
||||
const _: () = assert!(core::mem::size_of::<AgiContainerHeader>() == 64);
|
||||
|
||||
impl AgiContainerHeader {
|
||||
/// Check magic bytes.
|
||||
pub const fn is_valid_magic(&self) -> bool {
|
||||
self.magic == AGI_MAGIC
|
||||
}
|
||||
|
||||
/// Check if the container is signed.
|
||||
pub const fn is_signed(&self) -> bool {
|
||||
self.flags & AGI_SIGNED != 0
|
||||
}
|
||||
|
||||
/// Check if the container has a micro Linux kernel.
|
||||
pub const fn has_kernel(&self) -> bool {
|
||||
self.flags & AGI_HAS_KERNEL != 0
|
||||
}
|
||||
|
||||
/// Check if the container has an orchestrator config.
|
||||
pub const fn has_orchestrator(&self) -> bool {
|
||||
self.flags & AGI_HAS_ORCHESTRATOR != 0
|
||||
}
|
||||
|
||||
/// Check if the container supports replay mode.
|
||||
pub const fn is_replay_capable(&self) -> bool {
|
||||
self.flags & AGI_REPLAY_CAPABLE != 0
|
||||
}
|
||||
|
||||
/// Check if the container can run offline.
|
||||
pub const fn is_offline_capable(&self) -> bool {
|
||||
self.flags & AGI_OFFLINE_CAPABLE != 0
|
||||
}
|
||||
|
||||
/// Serialize header to a 64-byte array.
|
||||
pub fn to_bytes(&self) -> [u8; AGI_HEADER_SIZE] {
|
||||
let mut buf = [0u8; AGI_HEADER_SIZE];
|
||||
buf[0..4].copy_from_slice(&self.magic.to_le_bytes());
|
||||
buf[4..6].copy_from_slice(&self.version.to_le_bytes());
|
||||
buf[6..8].copy_from_slice(&self.flags.to_le_bytes());
|
||||
buf[8..24].copy_from_slice(&self.container_id);
|
||||
buf[24..40].copy_from_slice(&self.build_id);
|
||||
buf[40..48].copy_from_slice(&self.created_ns.to_le_bytes());
|
||||
buf[48..56].copy_from_slice(&self.model_id_hash);
|
||||
buf[56..64].copy_from_slice(&self.policy_hash);
|
||||
buf
|
||||
}
|
||||
|
||||
/// Deserialize header from a byte slice (>= 64 bytes).
|
||||
pub fn from_bytes(data: &[u8]) -> Result<Self, crate::RvfError> {
|
||||
if data.len() < AGI_HEADER_SIZE {
|
||||
return Err(crate::RvfError::SizeMismatch {
|
||||
expected: AGI_HEADER_SIZE,
|
||||
got: data.len(),
|
||||
});
|
||||
}
|
||||
let magic = u32::from_le_bytes([data[0], data[1], data[2], data[3]]);
|
||||
if magic != AGI_MAGIC {
|
||||
return Err(crate::RvfError::BadMagic {
|
||||
expected: AGI_MAGIC,
|
||||
got: magic,
|
||||
});
|
||||
}
|
||||
let mut container_id = [0u8; 16];
|
||||
container_id.copy_from_slice(&data[8..24]);
|
||||
let mut build_id = [0u8; 16];
|
||||
build_id.copy_from_slice(&data[24..40]);
|
||||
let mut model_id_hash = [0u8; 8];
|
||||
model_id_hash.copy_from_slice(&data[48..56]);
|
||||
let mut policy_hash = [0u8; 8];
|
||||
policy_hash.copy_from_slice(&data[56..64]);
|
||||
|
||||
Ok(Self {
|
||||
magic,
|
||||
version: u16::from_le_bytes([data[4], data[5]]),
|
||||
flags: u16::from_le_bytes([data[6], data[7]]),
|
||||
container_id,
|
||||
build_id,
|
||||
created_ns: u64::from_le_bytes([
|
||||
data[40], data[41], data[42], data[43],
|
||||
data[44], data[45], data[46], data[47],
|
||||
]),
|
||||
model_id_hash,
|
||||
policy_hash,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Required segments for a valid AGI container.
|
||||
///
|
||||
/// Used by the container builder/validator to ensure completeness.
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct ContainerSegments {
|
||||
/// KERNEL_SEG: micro Linux kernel (e.g. Firecracker-compatible vmlinux).
|
||||
pub kernel_present: bool,
|
||||
/// KERNEL_SEG size in bytes.
|
||||
pub kernel_size: u64,
|
||||
/// WASM_SEG: interpreter + microkernel modules.
|
||||
pub wasm_count: u16,
|
||||
/// Total WASM_SEG size in bytes.
|
||||
pub wasm_total_size: u64,
|
||||
/// VEC_SEG: world model vector count.
|
||||
pub vec_segment_count: u16,
|
||||
/// INDEX_SEG: HNSW index count.
|
||||
pub index_segment_count: u16,
|
||||
/// WITNESS_SEG: witness bundle count.
|
||||
pub witness_count: u32,
|
||||
/// CRYPTO_SEG: present.
|
||||
pub crypto_present: bool,
|
||||
/// META segment with AGI manifest: present.
|
||||
pub manifest_present: bool,
|
||||
/// Total container size in bytes.
|
||||
pub total_size: u64,
|
||||
}
|
||||
|
||||
impl ContainerSegments {
|
||||
/// Validate that the container has all required segments for a given
|
||||
/// execution mode.
|
||||
pub fn validate(&self, mode: ExecutionMode) -> Result<(), ContainerError> {
|
||||
// All modes require the manifest.
|
||||
if !self.manifest_present {
|
||||
return Err(ContainerError::MissingSegment("AGI manifest"));
|
||||
}
|
||||
|
||||
match mode {
|
||||
ExecutionMode::Replay => {
|
||||
// Replay needs witness chains.
|
||||
if self.witness_count == 0 {
|
||||
return Err(ContainerError::MissingSegment("witness chain"));
|
||||
}
|
||||
}
|
||||
ExecutionMode::Verify | ExecutionMode::Live => {
|
||||
// Verify/Live need at least kernel or WASM.
|
||||
if !self.kernel_present && self.wasm_count == 0 {
|
||||
return Err(ContainerError::MissingSegment(
|
||||
"kernel or WASM runtime",
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Compute the flags bitfield from present segments.
|
||||
pub fn to_flags(&self) -> u16 {
|
||||
let mut flags: u16 = 0;
|
||||
if self.kernel_present {
|
||||
flags |= AGI_HAS_KERNEL;
|
||||
}
|
||||
if self.wasm_count > 0 {
|
||||
flags |= AGI_HAS_WASM;
|
||||
}
|
||||
if self.witness_count > 0 {
|
||||
flags |= AGI_HAS_WITNESS;
|
||||
}
|
||||
if self.crypto_present {
|
||||
flags |= AGI_SIGNED;
|
||||
}
|
||||
flags
|
||||
}
|
||||
}
|
||||
|
||||
/// Error type for AGI container operations.
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum ContainerError {
|
||||
/// A required segment is missing.
|
||||
MissingSegment(&'static str),
|
||||
/// Container exceeds size limit.
|
||||
TooLarge { size: u64 },
|
||||
/// Invalid segment configuration.
|
||||
InvalidConfig(&'static str),
|
||||
/// Signature verification failed.
|
||||
SignatureInvalid,
|
||||
}
|
||||
|
||||
impl core::fmt::Display for ContainerError {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
match self {
|
||||
ContainerError::MissingSegment(s) => write!(f, "missing segment: {s}"),
|
||||
ContainerError::TooLarge { size } => write!(f, "container too large: {size} bytes"),
|
||||
ContainerError::InvalidConfig(s) => write!(f, "invalid config: {s}"),
|
||||
ContainerError::SignatureInvalid => write!(f, "signature verification failed"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn agi_header_size() {
|
||||
assert_eq!(core::mem::size_of::<AgiContainerHeader>(), 64);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn agi_header_round_trip() {
|
||||
let hdr = AgiContainerHeader {
|
||||
magic: AGI_MAGIC,
|
||||
version: 1,
|
||||
flags: AGI_HAS_KERNEL | AGI_HAS_ORCHESTRATOR | AGI_HAS_WORLD_MODEL
|
||||
| AGI_HAS_EVAL | AGI_SIGNED | AGI_REPLAY_CAPABLE,
|
||||
container_id: [0x42; 16],
|
||||
build_id: [0x43; 16],
|
||||
created_ns: 1_700_000_000_000_000_000,
|
||||
model_id_hash: [0xAA; 8],
|
||||
policy_hash: [0xBB; 8],
|
||||
};
|
||||
let bytes = hdr.to_bytes();
|
||||
assert_eq!(bytes.len(), AGI_HEADER_SIZE);
|
||||
let decoded = AgiContainerHeader::from_bytes(&bytes).unwrap();
|
||||
assert_eq!(decoded, hdr);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn agi_header_bad_magic() {
|
||||
let mut bytes = [0u8; 64];
|
||||
bytes[0..4].copy_from_slice(&0xDEADBEEFu32.to_le_bytes());
|
||||
assert!(AgiContainerHeader::from_bytes(&bytes).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn agi_header_too_short() {
|
||||
assert!(AgiContainerHeader::from_bytes(&[0u8; 32]).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn agi_flags() {
|
||||
let hdr = AgiContainerHeader {
|
||||
magic: AGI_MAGIC,
|
||||
version: 1,
|
||||
flags: AGI_HAS_KERNEL | AGI_HAS_ORCHESTRATOR | AGI_SIGNED,
|
||||
container_id: [0; 16],
|
||||
build_id: [0; 16],
|
||||
created_ns: 0,
|
||||
model_id_hash: [0; 8],
|
||||
policy_hash: [0; 8],
|
||||
};
|
||||
assert!(hdr.has_kernel());
|
||||
assert!(hdr.has_orchestrator());
|
||||
assert!(hdr.is_signed());
|
||||
assert!(!hdr.is_replay_capable());
|
||||
assert!(!hdr.is_offline_capable());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn execution_mode_round_trip() {
|
||||
for raw in 0..=2u8 {
|
||||
let m = ExecutionMode::try_from(raw).unwrap();
|
||||
assert_eq!(m as u8, raw);
|
||||
}
|
||||
assert!(ExecutionMode::try_from(3).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn segments_validate_replay_needs_witness() {
|
||||
let segs = ContainerSegments {
|
||||
manifest_present: true,
|
||||
witness_count: 0,
|
||||
..Default::default()
|
||||
};
|
||||
assert_eq!(
|
||||
segs.validate(ExecutionMode::Replay),
|
||||
Err(ContainerError::MissingSegment("witness chain"))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn segments_validate_live_needs_runtime() {
|
||||
let segs = ContainerSegments {
|
||||
manifest_present: true,
|
||||
kernel_present: false,
|
||||
wasm_count: 0,
|
||||
..Default::default()
|
||||
};
|
||||
assert_eq!(
|
||||
segs.validate(ExecutionMode::Live),
|
||||
Err(ContainerError::MissingSegment("kernel or WASM runtime"))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn segments_validate_live_with_kernel() {
|
||||
let segs = ContainerSegments {
|
||||
manifest_present: true,
|
||||
kernel_present: true,
|
||||
..Default::default()
|
||||
};
|
||||
assert!(segs.validate(ExecutionMode::Live).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn segments_validate_live_with_wasm() {
|
||||
let segs = ContainerSegments {
|
||||
manifest_present: true,
|
||||
wasm_count: 2,
|
||||
..Default::default()
|
||||
};
|
||||
assert!(segs.validate(ExecutionMode::Live).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn segments_validate_replay_with_witness() {
|
||||
let segs = ContainerSegments {
|
||||
manifest_present: true,
|
||||
witness_count: 10,
|
||||
..Default::default()
|
||||
};
|
||||
assert!(segs.validate(ExecutionMode::Replay).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn segments_to_flags() {
|
||||
let segs = ContainerSegments {
|
||||
kernel_present: true,
|
||||
wasm_count: 1,
|
||||
witness_count: 5,
|
||||
crypto_present: true,
|
||||
..Default::default()
|
||||
};
|
||||
let flags = segs.to_flags();
|
||||
assert_ne!(flags & AGI_HAS_KERNEL, 0);
|
||||
assert_ne!(flags & AGI_HAS_WASM, 0);
|
||||
assert_ne!(flags & AGI_HAS_WITNESS, 0);
|
||||
assert_ne!(flags & AGI_SIGNED, 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn container_error_display() {
|
||||
let e = ContainerError::MissingSegment("kernel");
|
||||
assert!(format!("{e}").contains("kernel"));
|
||||
let e2 = ContainerError::TooLarge { size: 999 };
|
||||
assert!(format!("{e2}").contains("999"));
|
||||
}
|
||||
}
|
||||
|
|
@ -44,6 +44,7 @@ pub mod sha256;
|
|||
pub mod ed25519;
|
||||
pub mod wasm_bootstrap;
|
||||
pub mod witness;
|
||||
pub mod agi_container;
|
||||
|
||||
pub use attestation::{AttestationHeader, AttestationWitnessType, TeePlatform, KEY_TYPE_TEE_BOUND};
|
||||
pub use ebpf::{
|
||||
|
|
@ -118,3 +119,10 @@ pub use wasm_bootstrap::{
|
|||
WASM_FEAT_REFERENCE_TYPES, WASM_FEAT_THREADS, WASM_FEAT_TAIL_CALL,
|
||||
WASM_FEAT_GC, WASM_FEAT_EXCEPTION_HANDLING,
|
||||
};
|
||||
pub use agi_container::{
|
||||
AgiContainerHeader, ContainerSegments, ContainerError, ExecutionMode,
|
||||
AGI_MAGIC, AGI_HEADER_SIZE,
|
||||
AGI_HAS_KERNEL, AGI_HAS_WASM, AGI_HAS_ORCHESTRATOR, AGI_HAS_WORLD_MODEL,
|
||||
AGI_HAS_EVAL, AGI_HAS_SKILLS, AGI_HAS_WITNESS, AGI_SIGNED,
|
||||
AGI_REPLAY_CAPABLE, AGI_OFFLINE_CAPABLE, AGI_HAS_TOOLS, AGI_HAS_COHERENCE_GATES,
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue