blob: 8c161588563e88658c2812709cf6779515887b42 [file]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
//! Agent Graph business policy. Host execution and plugin lifecycle are separate authorities.
pub mod access;
pub mod control;
pub mod coordinator;
pub mod decision;
pub mod owner;
pub mod projection;
pub mod repository;
pub mod schedule;
pub mod settings;
pub mod store;
pub mod swarm;
pub mod view;
use serde::{Deserialize, Serialize};
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("invalid Agent Graph input: {0}")]
Invalid(String),
#[error("Agent Graph is closed")]
Closed,
#[error("Agent Graph revision changed")]
Conflict,
#[error("Agent Graph item not found: {0}")]
NotFound(String),
#[error("Agent Graph decision outcome is unknown: {0}")]
OutcomeUnknown(String),
#[error("Agent Graph persistence failed: {0}")]
Persistence(String),
#[error(transparent)]
Storage(#[from] maka_capabilities::storage::StoreError),
#[error(transparent)]
Host(#[from] maka_capabilities::authorization::CommandError),
}
pub fn identity(value: &str) -> Result<(), Error> {
if value.is_empty() || value.trim() != value || value.chars().any(char::is_control) {
return Err(Error::Invalid("invalid identity".into()));
}
Ok(())
}
macro_rules! id {
($ty:ident, $prefix:literal) => {
#[derive(
Clone,
Debug,
PartialEq,
Eq,
PartialOrd,
Ord,
Serialize,
Deserialize,
schemars::JsonSchema,
)]
#[serde(try_from = "String", into = "String")]
#[schemars(with = "String", extend("minLength" = 1))]
pub struct $ty(String);
impl $ty {
pub fn new() -> Self {
Self(format!("{}{}", $prefix, uuid::Uuid::new_v4().simple()))
}
pub fn as_str(&self) -> &str {
&self.0
}
}
impl Default for $ty {
fn default() -> Self {
Self::new()
}
}
impl TryFrom<String> for $ty {
type Error = Error;
fn try_from(value: String) -> Result<Self, Error> {
identity(&value)?;
if !value.starts_with($prefix) {
return Err(Error::Invalid(concat!("invalid ", stringify!($ty)).into()));
}
Ok(Self(value))
}
}
impl From<$ty> for String {
fn from(id: $ty) -> Self {
id.0
}
}
impl std::fmt::Display for $ty {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(&self.0)
}
}
};
}
id!(GraphId, "agent_graph_");
id!(WorkId, "graph_work_");
id!(OperatorId, "graph_operator_");
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
#[serde(rename_all = "snake_case")]
#[schemars(rename = "GraphMode")]
pub enum Mode {
Graph,
Swarm,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
#[schemars(rename = "GraphEpoch")]
pub struct Epoch {
pub root_session_id: String,
pub epoch: u64,
pub graph_id: GraphId,
pub created_at: u64,
pub mode: Mode,
}
impl Epoch {
pub fn validate(&self) -> Result<(), Error> {
identity(&self.root_session_id)?;
if self.epoch == 0 || self.epoch > (1 << 53) - 1 || self.created_at > (1 << 53) - 1 {
return Err(Error::Invalid("invalid graph epoch or time".into()));
}
Ok(())
}
}