705 lines
20 KiB
Rust
705 lines
20 KiB
Rust
use schemars::JsonSchema;
|
|
use serde::{Deserialize, Serialize};
|
|
use std::collections::HashMap;
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
|
#[serde(rename_all = "lowercase")]
|
|
pub enum ChangeKind {
|
|
Added,
|
|
Modified,
|
|
Deleted,
|
|
Renamed,
|
|
}
|
|
|
|
impl Default for ChangeKind {
|
|
fn default() -> Self {
|
|
ChangeKind::Modified
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
|
pub struct CodeChange {
|
|
pub timestamp: u64,
|
|
pub file_path: String,
|
|
pub description: String,
|
|
#[serde(default)]
|
|
pub git_commit: Option<String>,
|
|
#[serde(default)]
|
|
pub git_branch: Option<String>,
|
|
#[serde(default)]
|
|
pub repo_name: Option<String>,
|
|
#[serde(default)]
|
|
pub repo_url: Option<String>,
|
|
#[serde(default = "default_namespace")]
|
|
pub namespace: String,
|
|
#[serde(default)]
|
|
pub change_kind: ChangeKind,
|
|
#[serde(default)]
|
|
pub symbols: Vec<String>,
|
|
#[serde(default)]
|
|
pub line_range: Option<String>,
|
|
#[serde(default)]
|
|
pub author: Option<String>,
|
|
#[serde(default)]
|
|
pub session_id: Option<String>,
|
|
#[serde(default)]
|
|
pub vcs_type: Option<String>,
|
|
#[serde(default)]
|
|
pub revision: Option<String>,
|
|
#[serde(default)]
|
|
pub branch: Option<String>,
|
|
#[serde(default)]
|
|
pub repository_root: Option<String>,
|
|
}
|
|
|
|
impl CodeChange {
|
|
pub fn effective_vcs(&self) -> &str {
|
|
if let Some(vcs) = &self.vcs_type {
|
|
return vcs.as_str();
|
|
}
|
|
if let Some(rev) = self.revision.as_deref().or(self.git_commit.as_deref()) {
|
|
if rev.starts_with('r') && rev[1..].chars().all(|c| c.is_ascii_digit()) {
|
|
return "svn";
|
|
}
|
|
}
|
|
if let Some(br) = self.branch.as_deref().or(self.git_branch.as_deref()) {
|
|
if br.eq_ignore_ascii_case("trunk")
|
|
|| br.starts_with("branches/")
|
|
|| br.starts_with("tags/")
|
|
{
|
|
return "svn";
|
|
}
|
|
}
|
|
"git"
|
|
}
|
|
|
|
pub fn effective_revision(&self) -> Option<&str> {
|
|
self.revision.as_deref().or(self.git_commit.as_deref())
|
|
}
|
|
|
|
pub fn effective_branch(&self) -> Option<&str> {
|
|
self.branch.as_deref().or(self.git_branch.as_deref())
|
|
}
|
|
}
|
|
|
|
pub fn generate_uuid() -> String {
|
|
uuid::Uuid::new_v4().to_string()
|
|
}
|
|
|
|
pub fn default_namespace() -> String {
|
|
"global".to_string()
|
|
}
|
|
|
|
pub fn normalize_entity_type(input: &str) -> String {
|
|
let trimmed = input.trim();
|
|
if trimmed.is_empty() {
|
|
return "Concept".to_string();
|
|
}
|
|
let clean = trimmed.replace(['_', '-'], " ");
|
|
let words: Vec<&str> = clean.split_whitespace().collect();
|
|
if words.is_empty() {
|
|
return "Concept".to_string();
|
|
}
|
|
|
|
let pascal: String = words
|
|
.into_iter()
|
|
.map(|w| {
|
|
let mut chars = w.chars();
|
|
match chars.next() {
|
|
None => String::new(),
|
|
Some(f) => f.to_uppercase().collect::<String>() + &chars.as_str().to_lowercase(),
|
|
}
|
|
})
|
|
.collect();
|
|
|
|
match pascal.as_str() {
|
|
"File" => "File".to_string(),
|
|
"McpTool" | "Mcptool" | "Mcp" => "McpTool".to_string(),
|
|
"Databasetable" | "DatabaseTable" => "DatabaseTable".to_string(),
|
|
"Datastructure" | "DataStore" | "Datastore" => "DataStructure".to_string(),
|
|
"Architecturecomponent" | "ArchitectureComponent" | "Architecture" => {
|
|
"ArchitectureComponent".to_string()
|
|
}
|
|
"Frontendcomponent" | "FrontendComponent" => "FrontendComponent".to_string(),
|
|
"Globalrule" | "GlobalRule" => "GlobalRule".to_string(),
|
|
"Securitypolicy" | "SecurityPolicy" => "SecurityPolicy".to_string(),
|
|
"Serviceaccount" | "ServiceAccount" => "ServiceAccount".to_string(),
|
|
"Testframework" | "TestFramework" => "TestFramework".to_string(),
|
|
"Toolhandler" | "ToolHandler" => "ToolHandler".to_string(),
|
|
"Historicalsummary" | "HistoricalSummary" => "HistoricalSummary".to_string(),
|
|
"Externalapi" | "ExternalApi" => "ExternalAPI".to_string(),
|
|
"Testsuite" | "TestSuite" => "TestSuite".to_string(),
|
|
"Confluencepage" | "ConfluencePage" => "ConfluencePage".to_string(),
|
|
"Containerimage" | "ContainerImage" => "ContainerImage".to_string(),
|
|
"Buildsystem" | "BuildSystem" => "BuildSystem".to_string(),
|
|
_ => pascal,
|
|
}
|
|
}
|
|
|
|
pub fn normalize_relation_type(input: &str) -> String {
|
|
let trimmed = input.trim();
|
|
if trimmed.is_empty() {
|
|
return "related_to".to_string();
|
|
}
|
|
trimmed.to_lowercase().replace([' ', '-'], "_")
|
|
}
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema, Default)]
|
|
#[serde(rename_all = "snake_case")]
|
|
pub enum TaskStatus {
|
|
Pending,
|
|
#[default]
|
|
Active,
|
|
InProgress,
|
|
Blocked,
|
|
Completed,
|
|
Cancelled,
|
|
}
|
|
|
|
impl TaskStatus {
|
|
pub fn is_active(&self) -> bool {
|
|
matches!(
|
|
self,
|
|
TaskStatus::Pending | TaskStatus::Active | TaskStatus::InProgress | TaskStatus::Blocked
|
|
)
|
|
}
|
|
|
|
pub fn is_completed(&self) -> bool {
|
|
matches!(self, TaskStatus::Completed)
|
|
}
|
|
|
|
pub fn as_str(&self) -> &'static str {
|
|
match self {
|
|
TaskStatus::Pending => "pending",
|
|
TaskStatus::Active => "active",
|
|
TaskStatus::InProgress => "in_progress",
|
|
TaskStatus::Blocked => "blocked",
|
|
TaskStatus::Completed => "completed",
|
|
TaskStatus::Cancelled => "cancelled",
|
|
}
|
|
}
|
|
}
|
|
|
|
impl std::fmt::Display for TaskStatus {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
write!(f, "{}", self.as_str())
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, Default)]
|
|
pub struct Entity {
|
|
pub name: String,
|
|
#[serde(alias = "entityType", alias = "type", alias = "category")]
|
|
pub entity_type: String,
|
|
#[serde(default)]
|
|
pub observations: Vec<String>,
|
|
#[serde(default = "default_namespace")]
|
|
pub namespace: String,
|
|
#[serde(default)]
|
|
pub git_branch: Option<String>,
|
|
#[serde(default)]
|
|
pub repo_name: Option<String>,
|
|
#[serde(default)]
|
|
pub file_path: Option<String>,
|
|
#[serde(default)]
|
|
pub created_at: Option<u64>,
|
|
#[serde(default)]
|
|
pub updated_at: Option<u64>,
|
|
#[serde(default)]
|
|
pub embedding: Option<Vec<f32>>,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, Default)]
|
|
pub struct Relation {
|
|
#[serde(alias = "source", alias = "from_node", alias = "source_node")]
|
|
pub from: String,
|
|
#[serde(alias = "target", alias = "to_node", alias = "target_node")]
|
|
pub to: String,
|
|
#[serde(alias = "relationType", alias = "type", alias = "relation")]
|
|
pub relation_type: String,
|
|
#[serde(default = "default_namespace")]
|
|
pub namespace: String,
|
|
#[serde(default)]
|
|
pub repo_name: Option<String>,
|
|
#[serde(default)]
|
|
pub weight: Option<f32>,
|
|
#[serde(default)]
|
|
pub is_bidirectional: bool,
|
|
}
|
|
|
|
impl PartialEq for Relation {
|
|
fn eq(&self, other: &Self) -> bool {
|
|
self.from == other.from
|
|
&& self.to == other.to
|
|
&& self.relation_type == other.relation_type
|
|
&& self.namespace == other.namespace
|
|
&& self.repo_name == other.repo_name
|
|
}
|
|
}
|
|
|
|
impl Eq for Relation {}
|
|
|
|
impl std::hash::Hash for Relation {
|
|
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
|
self.from.hash(state);
|
|
self.to.hash(state);
|
|
self.relation_type.hash(state);
|
|
self.namespace.hash(state);
|
|
self.repo_name.hash(state);
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
|
pub struct KnowledgeGraph {
|
|
#[serde(default)]
|
|
pub entities: HashMap<String, Entity>,
|
|
#[serde(default)]
|
|
pub relations: Vec<Relation>,
|
|
}
|
|
|
|
impl KnowledgeGraph {
|
|
/// Builds an adjacency index mapping node names to lists of outgoing/incoming neighbors and relation types.
|
|
pub fn build_adjacency_map<'a>(
|
|
&'a self,
|
|
relation_filter: Option<&'a str>,
|
|
) -> HashMap<&'a str, Vec<(&'a str, &'a str, bool)>> {
|
|
let mut adj: HashMap<&'a str, Vec<(&'a str, &'a str, bool)>> =
|
|
HashMap::with_capacity(self.relations.len() * 2);
|
|
for rel in &self.relations {
|
|
if let Some(rf) = relation_filter {
|
|
if rel.relation_type != rf {
|
|
continue;
|
|
}
|
|
}
|
|
adj.entry(rel.from.as_str()).or_default().push((
|
|
rel.to.as_str(),
|
|
rel.relation_type.as_str(),
|
|
false,
|
|
));
|
|
adj.entry(rel.to.as_str()).or_default().push((
|
|
rel.from.as_str(),
|
|
rel.relation_type.as_str(),
|
|
true,
|
|
));
|
|
}
|
|
adj
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
|
pub struct AcceptanceCriteria {
|
|
pub id: String,
|
|
pub description: String,
|
|
pub is_met: bool,
|
|
#[serde(default)]
|
|
pub verification_command: Option<String>,
|
|
#[serde(default)]
|
|
pub verification_output: Option<String>,
|
|
#[serde(default)]
|
|
pub verified_at: Option<u64>,
|
|
#[serde(default)]
|
|
pub verified_by: Option<String>,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
|
pub struct Task {
|
|
pub id: String,
|
|
pub title: String,
|
|
pub status: String,
|
|
pub description: String,
|
|
pub created_at: u64,
|
|
pub updated_at: u64,
|
|
pub git_branch: Option<String>,
|
|
#[serde(default)]
|
|
pub parent_id: Option<String>,
|
|
#[serde(default)]
|
|
pub expires_at: Option<u64>,
|
|
#[serde(default)]
|
|
pub dependencies: Vec<String>,
|
|
#[serde(default)]
|
|
pub acceptance_criteria: Vec<AcceptanceCriteria>,
|
|
#[serde(default = "default_namespace")]
|
|
pub namespace: String,
|
|
#[serde(default)]
|
|
pub repo_name: Option<String>,
|
|
#[serde(default)]
|
|
pub priority: Option<String>,
|
|
#[serde(default)]
|
|
pub assigned_agent: Option<String>,
|
|
#[serde(default)]
|
|
pub verification_command: Option<String>,
|
|
}
|
|
|
|
impl Task {
|
|
pub fn is_active(&self) -> bool {
|
|
let s = self.status.trim();
|
|
!s.eq_ignore_ascii_case("completed")
|
|
&& !s.eq_ignore_ascii_case("done")
|
|
&& !s.eq_ignore_ascii_case("cancelled")
|
|
&& !s.eq_ignore_ascii_case("canceled")
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
|
pub struct Snippet {
|
|
pub name: String,
|
|
pub language: String,
|
|
pub code: String,
|
|
pub description: String,
|
|
pub updated_at: u64,
|
|
#[serde(default)]
|
|
pub tags: Vec<String>,
|
|
#[serde(default)]
|
|
pub embedding: Option<Vec<f32>>,
|
|
#[serde(default = "default_namespace")]
|
|
pub namespace: String,
|
|
#[serde(default)]
|
|
pub repo_name: Option<String>,
|
|
#[serde(default)]
|
|
pub origin_file: Option<String>,
|
|
#[serde(default)]
|
|
pub line_range: Option<String>,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
|
pub struct Adr {
|
|
pub id: String,
|
|
pub title: String,
|
|
pub context: String,
|
|
pub decision: String,
|
|
#[serde(alias = "consequences")]
|
|
pub consequence: String,
|
|
#[serde(default = "default_adr_status")]
|
|
pub status: String,
|
|
#[serde(default)]
|
|
pub supersedes: Option<String>,
|
|
pub timestamp: u64,
|
|
#[serde(default = "default_namespace")]
|
|
pub namespace: String,
|
|
#[serde(default)]
|
|
pub repo_name: Option<String>,
|
|
#[serde(default)]
|
|
pub alternatives_considered: Vec<String>,
|
|
#[serde(default)]
|
|
pub affected_components: Vec<String>,
|
|
#[serde(default)]
|
|
pub author: Option<String>,
|
|
#[serde(default)]
|
|
pub git_commit: Option<String>,
|
|
#[serde(default)]
|
|
pub git_branch: Option<String>,
|
|
#[serde(default)]
|
|
pub resolved_at: Option<u64>,
|
|
#[serde(default)]
|
|
pub task_id: Option<String>,
|
|
}
|
|
|
|
fn default_adr_status() -> String {
|
|
"accepted".to_string()
|
|
}
|
|
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
|
pub struct ErrorFix {
|
|
pub signature: String,
|
|
pub solution: String,
|
|
pub timestamp: u64,
|
|
pub git_commit: Option<String>,
|
|
pub git_branch: Option<String>,
|
|
#[serde(default)]
|
|
pub embedding: Option<Vec<f32>>,
|
|
#[serde(default = "default_namespace")]
|
|
pub namespace: String,
|
|
#[serde(default)]
|
|
pub repo_name: Option<String>,
|
|
#[serde(default)]
|
|
pub error_category: Option<String>,
|
|
#[serde(default)]
|
|
pub stack_trace: Option<String>,
|
|
#[serde(default)]
|
|
pub toolchain: Option<String>,
|
|
}
|
|
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
|
pub struct SessionSummary {
|
|
pub timestamp: u64,
|
|
pub summary: String,
|
|
pub namespace: String,
|
|
#[serde(default)]
|
|
pub expires_at: Option<u64>,
|
|
#[serde(default)]
|
|
pub session_id: Option<String>,
|
|
#[serde(default)]
|
|
pub repo_name: Option<String>,
|
|
#[serde(default)]
|
|
pub git_branch: Option<String>,
|
|
#[serde(default)]
|
|
pub git_commit: Option<String>,
|
|
#[serde(default)]
|
|
pub tasks_completed: Vec<String>,
|
|
#[serde(default)]
|
|
pub next_steps: Vec<String>,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
|
pub struct HandoffMemo {
|
|
pub id: String,
|
|
pub author: String,
|
|
pub content: String,
|
|
#[serde(default)]
|
|
pub expires_at: Option<u64>,
|
|
pub namespace: String,
|
|
pub timestamp: u64,
|
|
#[serde(default)]
|
|
pub session_id: Option<String>,
|
|
#[serde(default)]
|
|
pub repo_name: Option<String>,
|
|
#[serde(default)]
|
|
pub git_branch: Option<String>,
|
|
#[serde(default)]
|
|
pub vcs_revision: Option<String>,
|
|
#[serde(default)]
|
|
pub blockers: Vec<String>,
|
|
#[serde(default)]
|
|
pub action_items: Vec<String>,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
|
pub struct EnvFingerprint {
|
|
pub namespace: String,
|
|
pub os: String,
|
|
pub shell: String,
|
|
pub tool_versions: HashMap<String, String>,
|
|
pub updated_at: u64,
|
|
#[serde(default)]
|
|
pub repo_name: Option<String>,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
|
pub struct EnvRequirement {
|
|
pub namespace: String,
|
|
pub key: String,
|
|
pub description: String,
|
|
pub is_secret: bool,
|
|
#[serde(default)]
|
|
pub repo_name: Option<String>,
|
|
#[serde(default)]
|
|
pub default_value: Option<String>,
|
|
#[serde(default)]
|
|
pub validation_regex: Option<String>,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
|
pub struct Milestone {
|
|
pub id: String,
|
|
pub title: String,
|
|
pub status: String,
|
|
pub namespace: String,
|
|
pub target_date: Option<u64>,
|
|
#[serde(default)]
|
|
pub description: Option<String>,
|
|
#[serde(default)]
|
|
pub repo_name: Option<String>,
|
|
#[serde(default)]
|
|
pub task_ids: Vec<String>,
|
|
#[serde(default)]
|
|
pub deliverables: Vec<String>,
|
|
#[serde(default)]
|
|
pub completed_at: Option<u64>,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
|
pub struct EnvironmentDetail {
|
|
pub namespace: String,
|
|
pub name: String,
|
|
pub url: String,
|
|
pub description: String,
|
|
pub requires_vpn: bool,
|
|
pub updated_at: u64,
|
|
#[serde(default)]
|
|
pub repo_name: Option<String>,
|
|
#[serde(default)]
|
|
pub env_type: Option<String>,
|
|
#[serde(default)]
|
|
pub healthcheck_endpoint: Option<String>,
|
|
#[serde(default)]
|
|
pub ssh_host: Option<String>,
|
|
}
|
|
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
|
pub struct TechDebt {
|
|
pub id: String,
|
|
pub namespace: String,
|
|
pub description: String,
|
|
pub ideal_solution: String,
|
|
pub is_resolved: bool,
|
|
pub created_at: u64,
|
|
pub git_commit: Option<String>,
|
|
pub git_branch: Option<String>,
|
|
#[serde(default)]
|
|
pub embedding: Option<Vec<f32>>,
|
|
#[serde(default)]
|
|
pub repo_name: Option<String>,
|
|
#[serde(default)]
|
|
pub severity: Option<String>,
|
|
#[serde(default)]
|
|
pub file_path: Option<String>,
|
|
#[serde(default)]
|
|
pub line_range: Option<String>,
|
|
#[serde(default)]
|
|
pub workaround: Option<String>,
|
|
#[serde(default)]
|
|
pub effort_estimate: Option<String>,
|
|
}
|
|
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
|
pub struct GateRecord {
|
|
pub id: String,
|
|
pub action: String,
|
|
pub target: String,
|
|
pub namespace: Option<String>,
|
|
pub params: std::collections::HashMap<String, String>,
|
|
pub status: String,
|
|
pub reason: Option<String>,
|
|
pub timestamp: u64,
|
|
#[serde(default)]
|
|
pub repo_name: Option<String>,
|
|
#[serde(default)]
|
|
pub gate_type: Option<String>,
|
|
#[serde(default)]
|
|
pub enforcer: Option<String>,
|
|
#[serde(default)]
|
|
pub validation_log: Option<String>,
|
|
#[serde(default)]
|
|
pub expires_at: Option<u64>,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
|
pub struct TerminalHistory {
|
|
#[serde(default = "generate_uuid")]
|
|
pub id: String,
|
|
pub command: String,
|
|
pub exit_code: i32,
|
|
pub cwd: String,
|
|
#[serde(default)]
|
|
pub os: String,
|
|
pub timestamp: u64,
|
|
#[serde(default)]
|
|
pub error_output: Option<String>,
|
|
#[serde(default)]
|
|
pub shell: Option<String>,
|
|
#[serde(default)]
|
|
pub duration_ms: Option<u64>,
|
|
#[serde(default)]
|
|
pub stdout_summary: Option<String>,
|
|
#[serde(default)]
|
|
pub status_reason: Option<String>,
|
|
#[serde(default)]
|
|
pub invoker: Option<String>,
|
|
#[serde(default)]
|
|
pub repo_name: Option<String>,
|
|
#[serde(default = "default_namespace")]
|
|
pub namespace: String,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
|
pub struct ActivityRecord {
|
|
pub timestamp: u64,
|
|
pub category: String, // e.g. "code_change", "task_update", "telemetry", "error_fix"
|
|
pub summary: String, // Brief human-readable description of what is being worked on
|
|
pub details: Option<String>,
|
|
#[serde(default)]
|
|
pub session_id: Option<String>,
|
|
#[serde(default)]
|
|
pub actor: Option<String>,
|
|
#[serde(default)]
|
|
pub repo_name: Option<String>,
|
|
#[serde(default = "default_namespace")]
|
|
pub namespace: String,
|
|
#[serde(default)]
|
|
pub associated_file: Option<String>,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
|
pub struct StateSnapshot {
|
|
pub id: String,
|
|
pub timestamp: u64,
|
|
pub description: String,
|
|
pub namespace: String,
|
|
#[serde(default)]
|
|
pub repo_name: Option<String>,
|
|
#[serde(default)]
|
|
pub git_branch: Option<String>,
|
|
#[serde(default)]
|
|
pub vcs_revision: Option<String>,
|
|
#[serde(default)]
|
|
pub active_tasks_count: usize,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
|
pub struct Hypothesis {
|
|
pub id: String,
|
|
pub task_id: Option<String>,
|
|
pub hypothesis: String,
|
|
pub status: String,
|
|
pub evidence: Option<String>,
|
|
pub timestamp: u64,
|
|
#[serde(default = "default_namespace")]
|
|
pub namespace: String,
|
|
#[serde(default)]
|
|
pub repo_name: Option<String>,
|
|
#[serde(default)]
|
|
pub validation_steps: Vec<String>,
|
|
#[serde(default)]
|
|
pub diagnostician: Option<String>,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
|
pub struct AgentSignal {
|
|
pub id: String,
|
|
pub sender: String,
|
|
pub signal_type: String,
|
|
pub payload: String,
|
|
pub timestamp: u64,
|
|
pub ttl_seconds: Option<u64>,
|
|
#[serde(default)]
|
|
pub recipient: Option<String>,
|
|
#[serde(default)]
|
|
pub correlation_id: Option<String>,
|
|
#[serde(default)]
|
|
pub priority: Option<String>,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
|
pub struct TaskEvent {
|
|
pub task_id: String,
|
|
pub status: String,
|
|
pub action: Option<String>,
|
|
pub result: Option<serde_json::Value>,
|
|
pub error: Option<String>,
|
|
pub timestamp: u64,
|
|
pub session_id: Option<String>,
|
|
#[serde(default)]
|
|
pub namespace: Option<String>,
|
|
#[serde(default)]
|
|
pub repo_name: Option<String>,
|
|
#[serde(default)]
|
|
pub actor: Option<String>,
|
|
}
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use crate::error::AppError;
|
|
use axum::http::StatusCode;
|
|
use axum::response::IntoResponse;
|
|
|
|
#[test]
|
|
fn test_app_error_bad_request_invalid_json() {
|
|
let err = AppError::BadRequest("invalid json".into());
|
|
let res = err.into_response();
|
|
assert_eq!(res.status(), StatusCode::BAD_REQUEST);
|
|
}
|
|
}
|