feat: add internal ZCLAW kernel crates to git tracking

This commit is contained in:
iven
2026-03-22 09:26:36 +08:00
parent d72c0f7161
commit 58cd24f85b
36 changed files with 10298 additions and 0 deletions

View File

@@ -0,0 +1,15 @@
[package]
name = "zclaw-types"
version.workspace = true
edition.workspace = true
license.workspace = true
repository.workspace = true
rust-version.workspace = true
description = "ZCLAW core type definitions"
[dependencies]
serde = { workspace = true }
serde_json = { workspace = true }
thiserror = { workspace = true }
uuid = { workspace = true }
chrono = { workspace = true }

View File

@@ -0,0 +1,165 @@
//! Agent configuration and state types
use serde::{Deserialize, Serialize};
use crate::{AgentId, Capability, ModelConfig};
/// Agent configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AgentConfig {
/// Unique identifier
pub id: AgentId,
/// Human-readable name
pub name: String,
/// Agent description
#[serde(default)]
pub description: Option<String>,
/// Model configuration
#[serde(default)]
pub model: ModelConfig,
/// System prompt
#[serde(default)]
pub system_prompt: Option<String>,
/// Capabilities granted to this agent
#[serde(default)]
pub capabilities: Vec<Capability>,
/// Tools available to this agent
#[serde(default)]
pub tools: Vec<String>,
/// Maximum tokens per response
#[serde(default)]
pub max_tokens: Option<u32>,
/// Temperature (0.0 - 1.0)
#[serde(default)]
pub temperature: Option<f32>,
/// Whether the agent is active
#[serde(default = "default_enabled")]
pub enabled: bool,
}
fn default_enabled() -> bool {
true
}
impl Default for AgentConfig {
fn default() -> Self {
Self {
id: AgentId::new(),
name: String::new(),
description: None,
model: ModelConfig::default(),
system_prompt: None,
capabilities: Vec::new(),
tools: Vec::new(),
max_tokens: None,
temperature: None,
enabled: true,
}
}
}
impl AgentConfig {
pub fn new(name: impl Into<String>) -> Self {
Self {
id: AgentId::new(),
name: name.into(),
..Default::default()
}
}
pub fn with_id(mut self, id: AgentId) -> Self {
self.id = id;
self
}
pub fn with_description(mut self, description: impl Into<String>) -> Self {
self.description = Some(description.into());
self
}
pub fn with_system_prompt(mut self, prompt: impl Into<String>) -> Self {
self.system_prompt = Some(prompt.into());
self
}
pub fn with_model(mut self, model: ModelConfig) -> Self {
self.model = model;
self
}
pub fn with_capabilities(mut self, capabilities: Vec<Capability>) -> Self {
self.capabilities = capabilities;
self
}
pub fn with_tools(mut self, tools: Vec<String>) -> Self {
self.tools = tools;
self
}
pub fn with_max_tokens(mut self, max_tokens: u32) -> Self {
self.max_tokens = Some(max_tokens);
self
}
pub fn with_temperature(mut self, temperature: f32) -> Self {
self.temperature = Some(temperature);
self
}
}
/// Agent runtime state
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum AgentState {
/// Agent is running and can receive messages
Running,
/// Agent is paused
Suspended,
/// Agent has been terminated
Terminated,
}
impl Default for AgentState {
fn default() -> Self {
Self::Running
}
}
impl std::fmt::Display for AgentState {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
AgentState::Running => write!(f, "running"),
AgentState::Suspended => write!(f, "suspended"),
AgentState::Terminated => write!(f, "terminated"),
}
}
}
/// Agent information for display
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AgentInfo {
pub id: AgentId,
pub name: String,
pub description: Option<String>,
pub model: String,
pub provider: String,
pub state: AgentState,
pub message_count: usize,
pub created_at: chrono::DateTime<chrono::Utc>,
pub updated_at: chrono::DateTime<chrono::Utc>,
}
impl From<AgentConfig> for AgentInfo {
fn from(config: AgentConfig) -> Self {
Self {
id: config.id,
name: config.name,
description: config.description,
model: config.model.model,
provider: config.model.provider,
state: AgentState::Running,
message_count: 0,
created_at: chrono::Utc::now(),
updated_at: chrono::Utc::now(),
}
}
}

View File

@@ -0,0 +1,158 @@
//! Capability-based security model
use serde::{Deserialize, Serialize};
/// A capability grants permission for a specific operation
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum Capability {
/// Invoke a specific tool
ToolInvoke { name: String },
/// Access to all tools
ToolAll,
/// Read from memory scope
MemoryRead { scope: String },
/// Write to memory scope
MemoryWrite { scope: String },
/// Connect to network host
NetConnect { host: String },
/// Execute shell commands matching pattern
ShellExec { pattern: String },
/// Spawn new agents
AgentSpawn,
/// Send messages to agents matching pattern
AgentMessage { pattern: String },
/// Kill agents matching pattern
AgentKill { pattern: String },
/// Discover remote peers via OFP
OfpDiscover,
/// Connect to specific OFP peers
OfpConnect { peer: String },
/// Advertise to OFP peers
OfpAdvertise,
}
impl Capability {
/// Create a tool invocation capability
pub fn tool(name: impl Into<String>) -> Self {
Self::ToolInvoke { name: name.into() }
}
/// Create a memory read capability
pub fn memory_read(scope: impl Into<String>) -> Self {
Self::MemoryRead { scope: scope.into() }
}
/// Create a memory write capability
pub fn memory_write(scope: impl Into<String>) -> Self {
Self::MemoryWrite { scope: scope.into() }
}
/// Create a network connect capability
pub fn net_connect(host: impl Into<String>) -> Self {
Self::NetConnect { host: host.into() }
}
/// Check if this capability grants access to a tool
pub fn allows_tool(&self, tool_name: &str) -> bool {
match self {
Capability::ToolAll => true,
Capability::ToolInvoke { name } => name == tool_name,
_ => false,
}
}
/// Check if this capability grants read access to a scope
pub fn allows_memory_read(&self, scope: &str) -> bool {
match self {
Capability::MemoryRead { scope: s } => {
s == "*" || s == scope || scope.starts_with(&format!("{}.", s))
}
_ => false,
}
}
/// Check if this capability grants write access to a scope
pub fn allows_memory_write(&self, scope: &str) -> bool {
match self {
Capability::MemoryWrite { scope: s } => {
s == "*" || s == scope || scope.starts_with(&format!("{}.", s))
}
_ => false,
}
}
}
/// Capability set for an agent
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct CapabilitySet {
pub capabilities: Vec<Capability>,
}
impl CapabilitySet {
pub fn new() -> Self {
Self { capabilities: Vec::new() }
}
pub fn with(mut self, capability: Capability) -> Self {
self.capabilities.push(capability);
self
}
pub fn with_all_tools(mut self) -> Self {
self.capabilities.push(Capability::ToolAll);
self
}
pub fn with_tool(mut self, name: impl Into<String>) -> Self {
self.capabilities.push(Capability::tool(name));
self
}
/// Check if any capability grants access to a tool
pub fn can_invoke_tool(&self, tool_name: &str) -> bool {
self.capabilities.iter().any(|c| c.allows_tool(tool_name))
}
/// Check if any capability grants read access to a scope
pub fn can_read_memory(&self, scope: &str) -> bool {
self.capabilities.iter().any(|c| c.allows_memory_read(scope))
}
/// Check if any capability grants write access to a scope
pub fn can_write_memory(&self, scope: &str) -> bool {
self.capabilities.iter().any(|c| c.allows_memory_write(scope))
}
/// Validate that a child's capabilities don't exceed parent's
pub fn validate_inheritance(&self, child: &CapabilitySet) -> bool {
// Child can only have capabilities that parent has
child.capabilities.iter().all(|child_cap| {
self.capabilities.iter().any(|parent_cap| {
child_cap == parent_cap || parent_cap.grants(child_cap)
})
})
}
}
impl Capability {
/// Check if this capability grants another capability
fn grants(&self, other: &Capability) -> bool {
match (self, other) {
// ToolAll grants any ToolInvoke
(Capability::ToolAll, Capability::ToolInvoke { .. }) => true,
// Wildcard scopes grant specific scopes
(Capability::MemoryRead { scope: a }, Capability::MemoryRead { scope: b }) => {
a == "*" || a == b || b.starts_with(&format!("{}.", a))
}
(Capability::MemoryWrite { scope: a }, Capability::MemoryWrite { scope: b }) => {
a == "*" || a == b || b.starts_with(&format!("{}.", a))
}
// NetConnect with "*" grants any host
(Capability::NetConnect { host: a }, Capability::NetConnect { host: b }) => {
a == "*" || a == b
}
_ => false,
}
}
}

View File

@@ -0,0 +1,52 @@
//! Error types for ZCLAW
use thiserror::Error;
/// ZCLAW unified error type
#[derive(Debug, Error)]
pub enum ZclawError {
#[error("Not found: {0}")]
NotFound(String),
#[error("Permission denied: {0}")]
PermissionDenied(String),
#[error("LLM error: {0}")]
LlmError(String),
#[error("Tool error: {0}")]
ToolError(String),
#[error("Storage error: {0}")]
StorageError(String),
#[error("Configuration error: {0}")]
ConfigError(String),
#[error("Serialization error: {0}")]
SerializationError(#[from] serde_json::Error),
#[error("IO error: {0}")]
IoError(#[from] std::io::Error),
#[error("HTTP error: {0}")]
HttpError(String),
#[error("Timeout: {0}")]
Timeout(String),
#[error("Invalid input: {0}")]
InvalidInput(String),
#[error("Agent loop detected: {0}")]
LoopDetected(String),
#[error("Rate limited: {0}")]
RateLimited(String),
#[error("Internal error: {0}")]
Internal(String),
}
/// Result type alias for ZCLAW operations
pub type Result<T> = std::result::Result<T, ZclawError>;

View File

@@ -0,0 +1,136 @@
//! Event types for ZCLAW event bus
use serde::{Deserialize, Serialize};
use crate::{AgentId, SessionId, RunId};
/// An event in the ZCLAW system
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum Event {
/// Kernel started
KernelStarted,
/// Kernel shutting down
KernelShutdown,
/// Agent spawned
AgentSpawned {
agent_id: AgentId,
name: String,
},
/// Agent terminated
AgentTerminated {
agent_id: AgentId,
reason: String,
},
/// Agent state changed
AgentStateChanged {
agent_id: AgentId,
old_state: String,
new_state: String,
},
/// Session created
SessionCreated {
session_id: SessionId,
agent_id: AgentId,
},
/// Message received
MessageReceived {
agent_id: AgentId,
session_id: SessionId,
role: String,
},
/// Message sent
MessageSent {
agent_id: AgentId,
session_id: SessionId,
role: String,
},
/// Tool invoked
ToolInvoked {
agent_id: AgentId,
tool_name: String,
},
/// Tool completed
ToolCompleted {
agent_id: AgentId,
tool_name: String,
success: bool,
duration_ms: u64,
},
/// Workflow started
WorkflowStarted {
workflow_id: String,
run_id: RunId,
},
/// Workflow completed
WorkflowCompleted {
workflow_id: String,
run_id: RunId,
success: bool,
},
/// Trigger fired
TriggerFired {
trigger_id: String,
trigger_type: String,
},
/// Skill loaded
SkillLoaded {
skill_id: String,
version: String,
},
/// Hand triggered
HandTriggered {
hand_name: String,
agent_id: Option<AgentId>,
},
/// Health check failed
HealthCheckFailed {
agent_id: AgentId,
reason: String,
},
/// Error occurred
Error {
source: String,
message: String,
},
}
impl Event {
/// Get the event type name
pub fn event_type(&self) -> &'static str {
match self {
Event::KernelStarted { .. } => "kernel_started",
Event::KernelShutdown { .. } => "kernel_shutdown",
Event::AgentSpawned { .. } => "agent_spawned",
Event::AgentTerminated { .. } => "agent_terminated",
Event::AgentStateChanged { .. } => "agent_state_changed",
Event::SessionCreated { .. } => "session_created",
Event::MessageReceived { .. } => "message_received",
Event::MessageSent { .. } => "message_sent",
Event::ToolInvoked { .. } => "tool_invoked",
Event::ToolCompleted { .. } => "tool_completed",
Event::WorkflowStarted { .. } => "workflow_started",
Event::WorkflowCompleted { .. } => "workflow_completed",
Event::TriggerFired { .. } => "trigger_fired",
Event::SkillLoaded { .. } => "skill_loaded",
Event::HandTriggered { .. } => "hand_triggered",
Event::HealthCheckFailed { .. } => "health_check_failed",
Event::Error { .. } => "error",
}
}
}

View File

@@ -0,0 +1,147 @@
//! ID types for ZCLAW entities
use serde::{Deserialize, Serialize};
use std::str::FromStr;
use uuid::Uuid;
/// Unique identifier for an Agent
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct AgentId(pub Uuid);
impl AgentId {
pub fn new() -> Self {
Self(Uuid::new_v4())
}
pub fn from_uuid(uuid: Uuid) -> Self {
Self(uuid)
}
pub fn as_uuid(&self) -> &Uuid {
&self.0
}
}
impl Default for AgentId {
fn default() -> Self {
Self::new()
}
}
impl std::fmt::Display for AgentId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
impl FromStr for AgentId {
type Err = uuid::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Uuid::parse_str(s).map(AgentId)
}
}
/// Unique identifier for a conversation session
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct SessionId(pub Uuid);
impl SessionId {
pub fn new() -> Self {
Self(Uuid::new_v4())
}
pub fn from_uuid(uuid: Uuid) -> Self {
Self(uuid)
}
pub fn as_uuid(&self) -> &Uuid {
&self.0
}
}
impl Default for SessionId {
fn default() -> Self {
Self::new()
}
}
impl std::fmt::Display for SessionId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
/// Unique identifier for a tool
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct ToolId(pub String);
impl ToolId {
pub fn new(name: impl Into<String>) -> Self {
Self(name.into())
}
pub fn as_str(&self) -> &str {
&self.0
}
}
impl std::fmt::Display for ToolId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
impl From<&str> for ToolId {
fn from(s: &str) -> Self {
Self(s.to_string())
}
}
impl From<String> for ToolId {
fn from(s: String) -> Self {
Self(s)
}
}
/// Unique identifier for a skill
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct SkillId(pub String);
impl SkillId {
pub fn new(name: impl Into<String>) -> Self {
Self(name.into())
}
pub fn as_str(&self) -> &str {
&self.0
}
}
impl std::fmt::Display for SkillId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
/// Unique identifier for a workflow run
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct RunId(pub Uuid);
impl RunId {
pub fn new() -> Self {
Self(Uuid::new_v4())
}
}
impl Default for RunId {
fn default() -> Self {
Self::new()
}
}
impl std::fmt::Display for RunId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}

View File

@@ -0,0 +1,24 @@
//! ZCLAW Core Types
//!
//! This crate defines the fundamental types used across all ZCLAW crates.
pub mod id;
pub mod message;
pub mod agent;
pub mod capability;
pub mod error;
pub mod event;
pub mod tool;
pub mod config;
pub use id::*;
pub use message::*;
pub use agent::*;
pub use capability::*;
pub use error::*;
pub use event::*;
pub use tool::*;
pub use config::*;
// Re-export commonly used external types
pub use serde_json::Value as JsonValue;

View File

@@ -0,0 +1,163 @@
//! Message types for Agent communication
use serde::{Deserialize, Serialize};
use serde_json::Value;
use crate::ToolId;
/// A message in a conversation
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "role", rename_all = "lowercase")]
pub enum Message {
/// User message
User {
content: String,
},
/// Assistant message
Assistant {
content: String,
/// Optional thinking/reasoning content
thinking: Option<String>,
},
/// Tool use request from the assistant
ToolUse {
id: String,
tool: ToolId,
input: Value,
},
/// Tool execution result
ToolResult {
tool_call_id: String,
tool: ToolId,
output: Value,
/// Whether the tool execution failed
is_error: bool,
},
/// System message (injected into context)
System {
content: String,
},
}
impl Message {
pub fn user(content: impl Into<String>) -> Self {
Self::User {
content: content.into(),
}
}
pub fn assistant(content: impl Into<String>) -> Self {
Self::Assistant {
content: content.into(),
thinking: None,
}
}
pub fn assistant_with_thinking(content: impl Into<String>, thinking: impl Into<String>) -> Self {
Self::Assistant {
content: content.into(),
thinking: Some(thinking.into()),
}
}
pub fn tool_use(id: impl Into<String>, tool: ToolId, input: Value) -> Self {
Self::ToolUse {
id: id.into(),
tool,
input,
}
}
pub fn tool_result(tool_call_id: impl Into<String>, tool: ToolId, output: Value, is_error: bool) -> Self {
Self::ToolResult {
tool_call_id: tool_call_id.into(),
tool,
output,
is_error,
}
}
pub fn system(content: impl Into<String>) -> Self {
Self::System {
content: content.into(),
}
}
/// Get the role name as a string
pub fn role(&self) -> &'static str {
match self {
Message::User { .. } => "user",
Message::Assistant { .. } => "assistant",
Message::ToolUse { .. } => "tool_use",
Message::ToolResult { .. } => "tool_result",
Message::System { .. } => "system",
}
}
/// Check if this is a user message
pub fn is_user(&self) -> bool {
matches!(self, Message::User { .. })
}
/// Check if this is an assistant message
pub fn is_assistant(&self) -> bool {
matches!(self, Message::Assistant { .. })
}
/// Check if this is a tool use
pub fn is_tool_use(&self) -> bool {
matches!(self, Message::ToolUse { .. })
}
/// Check if this is a tool result
pub fn is_tool_result(&self) -> bool {
matches!(self, Message::ToolResult { .. })
}
}
/// Content block for structured responses
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum ContentBlock {
Text { text: String },
Thinking { thinking: String },
ToolUse {
id: String,
name: String,
input: Value,
},
ToolResult {
tool_use_id: String,
content: String,
is_error: bool,
},
Image {
source: ImageSource,
},
}
/// Image source for multimodal messages
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ImageSource {
#[serde(rename = "type")]
pub source_type: String, // "base64", "url"
pub media_type: String,
pub data: String,
}
impl ImageSource {
pub fn base64(media_type: impl Into<String>, data: impl Into<String>) -> Self {
Self {
source_type: "base64".to_string(),
media_type: media_type.into(),
data: data.into(),
}
}
pub fn url(url: impl Into<String>) -> Self {
Self {
source_type: "url".to_string(),
media_type: "image/*".to_string(),
data: url.into(),
}
}
}