use std::collections::HashMap;
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum CrossFileEdgeType {
Imports,
Calls,
Registers,
Configures,
Tests,
Inherits,
}
#[derive(Clone, Debug, Deserialize)]
pub struct SeekInput {
pub query: String,
pub agent_id: String,
#[serde(default = "default_top_k")]
pub top_k: usize,
#[serde(default)]
pub scope: Option<String>,
#[serde(default)]
pub node_types: Vec<String>,
#[serde(default = "default_min_score")]
pub min_score: f32,
#[serde(default = "default_true")]
pub graph_rerank: bool,
}
#[derive(Clone, Debug, Serialize)]
pub struct SeekOutput {
pub query: String,
pub results: Vec<SeekResultEntry>,
pub total_candidates_scanned: usize,
pub embeddings_used: bool,
pub proof_state: String,
pub elapsed_ms: f64,
#[serde(skip_serializing_if = "Option::is_none")]
pub next_suggested_tool: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub next_suggested_target: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub next_step_hint: Option<String>,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct HeuristicSignals {
pub heuristic_factor: f32,
pub trust_score: f32,
pub trust_risk_multiplier: f32,
pub trust_tier: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub tremor_magnitude: Option<f32>,
pub tremor_observation_count: usize,
#[serde(skip_serializing_if = "Option::is_none")]
pub tremor_risk_level: Option<String>,
pub reason: String,
}
#[derive(Clone, Debug, Serialize)]
pub struct SeekResultEntry {
pub node_id: String,
pub label: String,
#[serde(rename = "type")]
pub node_type: String,
pub score: f32,
pub score_breakdown: SeekScoreBreakdown,
#[serde(skip_serializing_if = "Option::is_none")]
pub heuristic_signals: Option<HeuristicSignals>,
pub intent_summary: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub file_path: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub line_start: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub line_end: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub excerpt: Option<String>,
pub connections: Vec<SeekConnection>,
}
#[derive(Clone, Debug, Serialize)]
pub struct SeekScoreBreakdown {
pub embedding_similarity: f32,
pub graph_activation: f32,
pub temporal_recency: f32,
}
#[derive(Clone, Debug, Serialize)]
pub struct SeekConnection {
pub node_id: String,
pub label: String,
pub relation: String,
}
#[derive(Clone, Debug, Deserialize)]
pub struct ScanInput {
pub pattern: String,
pub agent_id: String,
#[serde(default)]
pub scope: Option<String>,
#[serde(default = "default_severity_min")]
pub severity_min: f32,
#[serde(default = "default_true")]
pub graph_validate: bool,
#[serde(default = "default_scan_limit")]
pub limit: usize,
}
#[derive(Clone, Debug, Serialize)]
pub struct ScanOutput {
pub pattern: String,
pub findings: Vec<ScanFinding>,
pub files_scanned: usize,
pub total_matches_raw: usize,
pub total_matches_validated: usize,
pub elapsed_ms: f64,
}
#[derive(Clone, Debug, Serialize)]
pub struct ScanFinding {
pub pattern: String,
pub status: String,
pub severity: f32,
pub node_id: String,
pub label: String,
pub file_path: String,
pub line: u32,
pub message: String,
pub graph_context: Vec<ScanContextNode>,
}
#[derive(Clone, Debug, Serialize)]
pub struct ScanContextNode {
pub node_id: String,
pub label: String,
pub relation: String,
}
#[derive(Clone, Debug, Deserialize)]
pub struct TimelineInput {
pub node: String,
pub agent_id: String,
#[serde(default = "default_depth_30d")]
pub depth: String,
#[serde(default = "default_true")]
pub include_co_changes: bool,
#[serde(default = "default_true")]
pub include_churn: bool,
#[serde(default = "default_top_k_10")]
pub top_k: usize,
}
#[derive(Clone, Debug, Serialize)]
pub struct TimelineOutput {
pub node: String,
pub depth: String,
pub proof_state: String,
pub changes: Vec<TimelineChange>,
pub co_changed_with: Vec<CoChangePartner>,
pub velocity: String,
pub stability_score: f32,
pub pattern: String,
pub total_churn: ChurnSummary,
pub commit_count_in_window: usize,
#[serde(skip_serializing_if = "Option::is_none")]
pub next_suggested_tool: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub next_suggested_target: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub next_step_hint: Option<String>,
pub elapsed_ms: f64,
}
#[derive(Clone, Debug, Serialize)]
pub struct TimelineChange {
pub date: String,
pub commit: String,
pub author: String,
pub subject: String,
pub delta: String,
pub co_changed: Vec<String>,
}
#[derive(Clone, Debug, Serialize)]
pub struct CoChangePartner {
pub file: String,
pub times: u32,
pub coupling_degree: f32,
}
#[derive(Clone, Debug, Serialize)]
pub struct ChurnSummary {
pub lines_added: u32,
pub lines_deleted: u32,
}
#[derive(Clone, Debug, Deserialize)]
pub struct DivergeInput {
pub agent_id: String,
pub baseline: String,
#[serde(default)]
pub scope: Option<String>,
#[serde(default = "default_true")]
pub include_coupling_changes: bool,
#[serde(default = "default_true")]
pub include_anomalies: bool,
}
#[derive(Clone, Debug, Serialize)]
pub struct DivergeOutput {
pub baseline: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub baseline_commit: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub scope: Option<String>,
pub structural_drift: f32,
pub new_nodes: Vec<String>,
pub removed_nodes: Vec<String>,
pub modified_nodes: Vec<DivergeModifiedNode>,
pub coupling_changes: Vec<CouplingChange>,
pub anomalies: Vec<DivergeAnomaly>,
pub summary: String,
pub elapsed_ms: f64,
}
#[derive(Clone, Debug, Serialize)]
pub struct DivergeModifiedNode {
pub file: String,
pub delta: String,
pub growth_ratio: f32,
}
#[derive(Clone, Debug, Serialize)]
pub struct CouplingChange {
pub pair: [String; 2],
pub was: f32,
pub now: f32,
pub direction: String,
}
#[derive(Clone, Debug, Serialize)]
pub struct DivergeAnomaly {
#[serde(rename = "type")]
pub anomaly_type: String,
pub file: String,
pub detail: String,
pub severity: String,
}
#[derive(Clone, Debug, Deserialize)]
pub struct TrailSaveInput {
pub agent_id: String,
pub label: String,
#[serde(default)]
pub hypotheses: Vec<TrailHypothesisInput>,
#[serde(default)]
pub conclusions: Vec<TrailConclusionInput>,
#[serde(default)]
pub open_questions: Vec<String>,
#[serde(default)]
pub tags: Vec<String>,
#[serde(default)]
pub summary: Option<String>,
#[serde(default)]
pub visited_nodes: Vec<TrailVisitedNodeInput>,
#[serde(default)]
pub activation_boosts: HashMap<String, f32>,
}
#[derive(Clone, Debug, Deserialize)]
pub struct TrailHypothesisInput {
pub statement: String,
#[serde(default = "default_confidence")]
pub confidence: f32,
#[serde(default)]
pub supporting_nodes: Vec<String>,
#[serde(default)]
pub contradicting_nodes: Vec<String>,
}
#[derive(Clone, Debug, Deserialize)]
pub struct TrailConclusionInput {
pub statement: String,
#[serde(default = "default_confidence")]
pub confidence: f32,
#[serde(default)]
pub from_hypotheses: Vec<String>,
#[serde(default)]
pub supporting_nodes: Vec<String>,
}
#[derive(Clone, Debug, Deserialize)]
pub struct TrailVisitedNodeInput {
pub node_external_id: String,
#[serde(default)]
pub annotation: Option<String>,
#[serde(default = "default_relevance")]
pub relevance: f32,
}
#[derive(Clone, Debug, Serialize)]
pub struct TrailSaveOutput {
pub trail_id: String,
pub label: String,
pub agent_id: String,
pub nodes_saved: usize,
pub hypotheses_saved: usize,
pub conclusions_saved: usize,
pub open_questions_saved: usize,
pub graph_generation_at_creation: u64,
pub created_at_ms: u64,
}
#[derive(Clone, Debug, Deserialize)]
pub struct TrailResumeInput {
pub agent_id: String,
pub trail_id: String,
#[serde(default)]
pub force: bool,
#[serde(default = "default_top_k_5")]
pub max_reactivated_nodes: usize,
#[serde(default = "default_top_k_4")]
pub max_resume_hints: usize,
}
#[derive(Clone, Debug, Serialize)]
pub struct TrailResumeOutput {
pub trail_id: String,
pub label: String,
pub stale: bool,
pub generations_behind: u64,
pub missing_nodes: Vec<String>,
pub nodes_reactivated: usize,
pub reactivated_node_ids: Vec<String>,
pub hypotheses_downgraded: Vec<String>,
pub next_focus_node_id: Option<String>,
pub next_open_question: Option<String>,
pub next_suggested_tool: Option<String>,
pub resume_hints: Vec<String>,
pub trail: TrailSummaryOutput,
pub elapsed_ms: f64,
}
#[derive(Clone, Debug, Serialize)]
pub struct TrailSummaryOutput {
pub trail_id: String,
pub agent_id: String,
pub label: String,
pub status: String,
pub created_at_ms: u64,
pub last_modified_ms: u64,
pub node_count: usize,
pub hypothesis_count: usize,
pub conclusion_count: usize,
pub open_question_count: usize,
pub tags: Vec<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub summary: Option<String>,
}
#[derive(Clone, Debug, Deserialize)]
pub struct TrailMergeInput {
pub agent_id: String,
pub trail_ids: Vec<String>,
#[serde(default)]
pub label: Option<String>,
}
#[derive(Clone, Debug, Serialize)]
pub struct TrailMergeOutput {
pub merged_trail_id: String,
pub label: String,
pub source_trails: Vec<String>,
pub nodes_merged: usize,
pub hypotheses_merged: usize,
pub conflicts: Vec<TrailMergeConflict>,
pub connections_discovered: Vec<TrailConnection>,
pub elapsed_ms: f64,
}
#[derive(Clone, Debug, Serialize)]
pub struct TrailMergeConflict {
pub hypothesis_a: String,
pub hypothesis_b: String,
pub resolution: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub winner: Option<String>,
pub score_delta: f32,
}
#[derive(Clone, Debug, Serialize)]
pub struct TrailConnection {
#[serde(rename = "type")]
pub connection_type: String,
pub detail: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub from_node: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub to_node: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub weight: Option<f32>,
}
#[derive(Clone, Debug, Deserialize)]
pub struct TrailListInput {
pub agent_id: String,
#[serde(default)]
pub filter_agent_id: Option<String>,
#[serde(default)]
pub filter_status: Option<String>,
#[serde(default)]
pub filter_tags: Vec<String>,
}
#[derive(Clone, Debug, Serialize)]
pub struct TrailListOutput {
pub trails: Vec<TrailSummaryOutput>,
pub total_count: usize,
}
#[derive(Clone, Debug, Deserialize)]
pub struct HypothesizeInput {
pub claim: String,
pub agent_id: String,
#[serde(default = "default_max_hops")]
pub max_hops: u8,
#[serde(default = "default_true")]
pub include_ghost_edges: bool,
#[serde(default = "default_true")]
pub include_partial_flow: bool,
#[serde(default = "default_path_budget")]
pub path_budget: usize,
}
#[derive(Clone, Debug, Serialize)]
pub struct HypothesizeOutput {
pub claim: String,
pub claim_type: String,
pub subject_nodes: Vec<String>,
pub object_nodes: Vec<String>,
pub verdict: String,
pub confidence: f32,
pub proof_state: String,
pub supporting_evidence: Vec<HypothesisEvidence>,
pub contradicting_evidence: Vec<HypothesisEvidence>,
#[serde(skip_serializing_if = "Option::is_none")]
pub partial_reach: Option<Vec<PartialReachEntry>>,
pub paths_explored: usize,
pub elapsed_ms: f64,
#[serde(skip_serializing_if = "Option::is_none")]
pub next_suggested_tool: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub next_suggested_target: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub next_step_hint: Option<String>,
}
#[derive(Clone, Debug, Serialize)]
pub struct HypothesisEvidence {
#[serde(rename = "type")]
pub evidence_type: String,
pub description: String,
pub likelihood_factor: f32,
pub nodes: Vec<String>,
#[serde(default)]
pub relations: Vec<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub path_weight: Option<f32>,
}
#[derive(Clone, Debug, Serialize)]
pub struct PartialReachEntry {
pub node_id: String,
pub label: String,
pub hops_from_source: u8,
pub activation_at_stop: f32,
}
#[derive(Clone, Debug, Deserialize)]
pub struct DifferentialInput {
pub agent_id: String,
pub snapshot_a: String,
pub snapshot_b: String,
#[serde(default)]
pub question: Option<String>,
#[serde(default)]
pub focus_nodes: Vec<String>,
}
#[derive(Clone, Debug, Serialize)]
pub struct DifferentialOutput {
pub snapshot_a: String,
pub snapshot_b: String,
pub new_edges: Vec<DiffEdgeDelta>,
pub removed_edges: Vec<DiffEdgeDelta>,
pub weight_changes: Vec<DiffWeightDelta>,
pub new_nodes: Vec<String>,
pub removed_nodes: Vec<String>,
pub coupling_deltas: Vec<DiffCouplingDelta>,
pub summary: String,
pub elapsed_ms: f64,
}
#[derive(Clone, Debug, Serialize)]
pub struct DiffEdgeDelta {
pub source: String,
pub target: String,
pub relation: String,
pub weight: f32,
}
#[derive(Clone, Debug, Serialize)]
pub struct DiffWeightDelta {
pub source: String,
pub target: String,
pub relation: String,
pub old_weight: f32,
pub new_weight: f32,
pub delta: f32,
}
#[derive(Clone, Debug, Serialize)]
pub struct DiffCouplingDelta {
pub community_a: String,
pub community_b: String,
pub old_coupling: f32,
pub new_coupling: f32,
pub delta: f32,
}
#[derive(Clone, Debug, Deserialize)]
pub struct TraceInput {
pub error_text: String,
pub agent_id: String,
#[serde(default)]
pub language: Option<String>,
#[serde(default = "default_window_hours")]
pub window_hours: f32,
#[serde(default = "default_top_k_10")]
pub top_k: usize,
}
#[derive(Clone, Debug, Serialize)]
pub struct TraceOutput {
pub language_detected: String,
pub error_type: String,
pub error_message: String,
pub frames_parsed: usize,
pub frames_mapped: usize,
pub proof_state: String,
pub suspects: Vec<TraceSuspect>,
pub co_change_suspects: Vec<TraceCoChangeSuspect>,
pub causal_chain: Vec<String>,
pub fix_scope: TraceFixScope,
#[serde(skip_serializing_if = "Option::is_none")]
pub next_suggested_tool: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub next_suggested_target: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub next_step_hint: Option<String>,
pub unmapped_frames: Vec<TraceUnmappedFrame>,
pub elapsed_ms: f64,
}
#[derive(Clone, Debug, Serialize)]
pub struct TraceSuspect {
pub node_id: String,
pub label: String,
#[serde(rename = "type")]
pub node_type: String,
pub suspiciousness: f32,
pub signals: TraceSuspiciousnessSignals,
#[serde(skip_serializing_if = "Option::is_none")]
pub file_path: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub line_start: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub line_end: Option<u32>,
pub related_callers: Vec<String>,
}
#[derive(Clone, Debug, Serialize)]
pub struct TraceSuspiciousnessSignals {
pub trace_depth_score: f32,
pub recency_score: f32,
pub centrality_score: f32,
}
#[derive(Clone, Debug, Serialize)]
pub struct TraceCoChangeSuspect {
pub node_id: String,
pub label: String,
pub modified_at: f64,
pub reason: String,
}
#[derive(Clone, Debug, Serialize)]
pub struct TraceFixScope {
pub files_to_inspect: Vec<String>,
pub estimated_blast_radius: usize,
pub risk_level: String,
}
#[derive(Clone, Debug, Serialize)]
pub struct TraceUnmappedFrame {
pub file: String,
pub line: u32,
pub function: String,
pub reason: String,
}
#[derive(Clone, Debug, Deserialize)]
pub struct ValidatePlanInput {
pub agent_id: String,
pub actions: Vec<PlannedAction>,
#[serde(default = "default_true")]
pub include_test_impact: bool,
#[serde(default = "default_true")]
pub include_risk_score: bool,
}
#[derive(Clone, Debug, Deserialize)]
pub struct PlannedAction {
pub action_type: String,
pub file_path: String,
#[serde(default)]
pub description: Option<String>,
#[serde(default)]
pub depends_on: Vec<String>,
}
#[derive(Clone, Debug, Serialize)]
pub struct ValidatePlanOutput {
pub actions_analyzed: usize,
pub actions_resolved: usize,
pub actions_unresolved: usize,
pub gaps: Vec<PlanGap>,
pub risk_score: f32,
pub risk_level: String,
pub proof_state: String,
pub test_coverage: PlanTestCoverage,
pub suggested_additions: Vec<PlanSuggestedAction>,
pub blast_radius_total: usize,
#[serde(skip_serializing_if = "Option::is_none")]
pub heuristic_summary: Option<PlanHeuristicSummary>,
#[serde(skip_serializing_if = "Option::is_none")]
pub next_suggested_tool: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub next_suggested_target: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub next_step_hint: Option<String>,
pub elapsed_ms: f64,
}
#[derive(Clone, Debug, Serialize)]
pub struct PlanGap {
pub file_path: String,
pub node_id: String,
pub reason: String,
pub severity: String,
pub signal_strength: f32,
pub antibody_hits: usize,
#[serde(skip_serializing_if = "Option::is_none")]
pub heuristic_signals: Option<HeuristicSignals>,
#[serde(skip_serializing_if = "Option::is_none")]
pub heuristics_surface_ref: Option<HeuristicsSurfaceRef>,
}
#[derive(Clone, Debug, Serialize)]
pub struct PlanTestCoverage {
pub modified_files: usize,
pub tested_files: usize,
pub untested_files: Vec<String>,
pub coverage_ratio: f32,
}
#[derive(Clone, Debug, Serialize)]
pub struct PlanSuggestedAction {
pub action_type: String,
pub file_path: String,
pub reason: String,
}
#[derive(Clone, Debug, Serialize)]
pub struct PlanHeuristicSummary {
pub heuristic_risk: f32,
pub hotspot_count: usize,
pub low_trust_hotspots: usize,
pub tremor_hotspots: usize,
pub antibody_hotspots: usize,
pub hotspots: Vec<PlanHeuristicHotspot>,
}
#[derive(Clone, Debug, Serialize)]
pub struct PlanHeuristicHotspot {
pub file_path: String,
pub node_id: String,
pub role: String,
pub antibody_hits: usize,
pub proof_hint: String,
pub heuristic_signals: HeuristicSignals,
pub heuristics_surface_ref: HeuristicsSurfaceRef,
}
#[derive(Clone, Debug, Serialize)]
pub struct HeuristicsSurfaceRef {
pub node_id: String,
pub file_path: String,
}
#[derive(Clone, Debug, Deserialize)]
pub struct FederateInput {
pub agent_id: String,
pub repos: Vec<FederateRepo>,
#[serde(default = "default_true")]
pub detect_cross_repo_edges: bool,
#[serde(default)]
pub incremental: bool,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct FederateRepo {
pub name: String,
pub path: String,
#[serde(default = "default_adapter")]
pub adapter: String,
}
#[derive(Clone, Debug, Serialize)]
pub struct FederateOutput {
pub repos_ingested: Vec<FederateRepoResult>,
pub total_nodes: u32,
pub total_edges: u64,
pub cross_repo_edges: Vec<FederateCrossRepoEdge>,
pub cross_repo_edge_count: usize,
pub incremental: bool,
pub skipped_repos: Vec<String>,
pub elapsed_ms: f64,
}
#[derive(Clone, Debug, Serialize)]
pub struct FederateRepoResult {
pub name: String,
pub path: String,
pub node_count: u32,
pub edge_count: u32,
pub from_cache: bool,
pub ingest_ms: f64,
}
#[derive(Clone, Debug, Serialize)]
pub struct FederateCrossRepoEdge {
pub source_repo: String,
pub target_repo: String,
pub source_node: String,
pub target_node: String,
pub edge_type: String,
pub relation: String,
pub weight: f32,
pub causal_strength: f32,
}
#[derive(Clone, Debug, Deserialize)]
pub struct GhostEdgesInput {
pub agent_id: String,
#[serde(default = "default_depth_30d")]
pub depth: String,
#[serde(default)]
pub scope: Option<String>,
#[serde(default = "default_scan_limit")]
pub top_k: usize,
}
#[derive(Clone, Debug, Deserialize)]
pub struct TaintTraceInput {
pub agent_id: String,
pub entry_nodes: Vec<String>,
#[serde(default = "default_taint_type")]
pub taint_type: String,
#[serde(default)]
pub boundary_patterns: Vec<String>,
#[serde(default = "default_taint_max_depth")]
pub max_depth: u32,
#[serde(default = "default_taint_min_prob")]
pub min_probability: f32,
}
fn default_taint_type() -> String {
"user_input".to_string()
}
fn default_taint_max_depth() -> u32 {
15
}
fn default_taint_min_prob() -> f32 {
0.01
}
#[derive(Clone, Debug, Deserialize)]
pub struct TwinsInput {
pub agent_id: String,
#[serde(default = "default_twin_threshold")]
pub similarity_threshold: f32,
#[serde(default = "default_scan_limit")]
pub top_k: usize,
#[serde(default)]
pub scope: Option<String>,
#[serde(default)]
pub node_types: Vec<String>,
}
fn default_twin_threshold() -> f32 {
0.80
}
#[derive(Clone, Debug, Deserialize)]
pub struct RefactorPlanInput {
pub agent_id: String,
#[serde(default)]
pub scope: Option<String>,
#[serde(default = "default_max_communities")]
pub max_communities: usize,
#[serde(default = "default_min_community_size")]
pub min_community_size: usize,
}
fn default_max_communities() -> usize {
10
}
fn default_min_community_size() -> usize {
3
}
#[derive(Clone, Debug, Deserialize)]
pub struct RuntimeOverlayInput {
pub agent_id: String,
pub spans: Vec<RuntimeOverlaySpan>,
#[serde(default)]
pub service_name: String,
#[serde(default = "default_mapping_strategy")]
pub mapping_strategy: String,
#[serde(default = "default_boost_strength")]
pub boost_strength: f32,
}
#[derive(Clone, Debug, Deserialize)]
pub struct RuntimeOverlaySpan {
pub name: String,
pub duration_us: u64,
#[serde(default = "default_span_count")]
pub count: u64,
#[serde(default)]
pub is_error: bool,
#[serde(default)]
pub attributes: std::collections::HashMap<String, String>,
pub parent: Option<String>,
}
fn default_mapping_strategy() -> String {
"label_match".to_string()
}
fn default_boost_strength() -> f32 {
0.15
}
fn default_span_count() -> u64 {
1
}
fn default_top_k() -> usize {
20
}
fn default_top_k_5() -> usize {
5
}
fn default_top_k_4() -> usize {
4
}
fn default_top_k_10() -> usize {
10
}
fn default_true() -> bool {
true
}
fn default_max_hops() -> u8 {
5
}
fn default_min_score() -> f32 {
0.1
}
fn default_severity_min() -> f32 {
0.3
}
fn default_scan_limit() -> usize {
50
}
fn default_depth_30d() -> String {
"30d".into()
}
fn default_confidence() -> f32 {
0.5
}
fn default_relevance() -> f32 {
0.5
}
fn default_path_budget() -> usize {
1000
}
fn default_window_hours() -> f32 {
24.0
}
fn default_adapter() -> String {
"code".into()
}
#[derive(Clone, Debug, Deserialize)]
pub struct AntibodyScanInput {
pub agent_id: String,
#[serde(default = "default_scope_all")]
pub scope: String,
#[serde(default)]
pub antibody_ids: Vec<String>,
#[serde(default = "default_scan_limit")]
pub max_matches: usize,
#[serde(default = "default_severity_info")]
pub min_severity: String,
#[serde(default = "default_similarity_threshold")]
pub similarity_threshold: f32,
#[serde(default = "default_match_mode")]
pub match_mode: String,
#[serde(default = "default_max_matches_per_antibody")]
pub max_matches_per_antibody: usize,
}
fn default_scope_all() -> String {
"all".to_string()
}
fn default_severity_info() -> String {
"info".to_string()
}
fn default_similarity_threshold() -> f32 {
0.7
}
fn default_match_mode() -> String {
"substring".to_string()
}
fn default_max_matches_per_antibody() -> usize {
50
}
#[derive(Clone, Debug, Deserialize)]
pub struct AntibodyListInput {
pub agent_id: String,
#[serde(default)]
pub include_disabled: bool,
}
#[derive(Clone, Debug, Deserialize)]
pub struct AntibodyCreateInput {
pub agent_id: String,
#[serde(default = "default_action_create")]
pub action: String,
pub antibody_id: Option<String>,
pub name: Option<String>,
pub description: Option<String>,
#[serde(default = "default_severity_warning")]
pub severity: String,
pub pattern: Option<AntibodyPatternInput>,
}
fn default_action_create() -> String {
"create".to_string()
}
fn default_severity_warning() -> String {
"warning".to_string()
}
#[derive(Clone, Debug, Deserialize)]
pub struct AntibodyPatternInput {
pub nodes: Vec<PatternNodeInput>,
pub edges: Vec<PatternEdgeInput>,
#[serde(default)]
pub negative_edges: Vec<PatternEdgeInput>,
}
#[derive(Clone, Debug, Deserialize)]
pub struct PatternNodeInput {
pub role: String,
pub node_type: Option<String>,
#[serde(default)]
pub required_tags: Vec<String>,
pub label_contains: Option<String>,
}
#[derive(Clone, Debug, Deserialize)]
pub struct PatternEdgeInput {
pub source_idx: usize,
pub target_idx: usize,
pub relation: Option<String>,
}
#[derive(Clone, Debug, Deserialize)]
pub struct FlowSimulateInput {
pub agent_id: String,
#[serde(default)]
pub entry_nodes: Vec<String>,
#[serde(default = "default_num_particles")]
pub num_particles: u32,
#[serde(default)]
pub lock_patterns: Vec<String>,
#[serde(default)]
pub read_only_patterns: Vec<String>,
#[serde(default = "default_flow_max_depth")]
pub max_depth: u8,
#[serde(default = "default_turbulence_threshold")]
pub turbulence_threshold: f32,
#[serde(default = "default_true")]
pub include_paths: bool,
#[serde(default = "default_max_total_steps")]
pub max_total_steps: usize,
#[serde(default)]
pub scope_filter: Option<String>,
}
fn default_num_particles() -> u32 {
2
}
fn default_flow_max_depth() -> u8 {
15
}
fn default_turbulence_threshold() -> f32 {
0.5
}
fn default_max_total_steps() -> usize {
50000
}
#[derive(Clone, Debug, Deserialize)]
pub struct EpidemicInput {
pub agent_id: String,
pub infected_nodes: Vec<String>,
#[serde(default)]
pub recovered_nodes: Vec<String>,
pub infection_rate: Option<f32>,
#[serde(default)]
pub recovery_rate: f32,
#[serde(default = "default_epidemic_iterations")]
pub iterations: u32,
#[serde(default = "default_direction_both")]
pub direction: String,
#[serde(default = "default_top_k")]
pub top_k: usize,
#[serde(default = "default_true")]
pub auto_calibrate: bool,
#[serde(default = "default_scope_all")]
pub scope: String,
#[serde(default = "default_min_probability")]
pub min_probability: f32,
}
fn default_epidemic_iterations() -> u32 {
50
}
fn default_direction_both() -> String {
"both".to_string()
}
fn default_min_probability() -> f32 {
0.001
}
#[derive(Clone, Debug, Deserialize)]
pub struct TremorInput {
pub agent_id: String,
#[serde(default = "default_tremor_window")]
pub window: String,
#[serde(default = "default_tremor_threshold")]
pub threshold: f32,
#[serde(default = "default_top_k")]
pub top_k: usize,
pub node_filter: Option<String>,
#[serde(default)]
pub include_history: bool,
#[serde(default = "default_min_observations")]
pub min_observations: usize,
#[serde(default = "default_sensitivity")]
pub sensitivity: f32,
}
fn default_tremor_window() -> String {
"30d".to_string()
}
fn default_tremor_threshold() -> f32 {
0.1
}
fn default_min_observations() -> usize {
3
}
fn default_sensitivity() -> f32 {
1.0
}
#[derive(Clone, Debug, Deserialize)]
pub struct TrustInput {
pub agent_id: String,
#[serde(default = "default_scope_file")]
pub scope: String,
#[serde(default = "default_min_history")]
pub min_history: u32,
#[serde(default = "default_top_k")]
pub top_k: usize,
pub node_filter: Option<String>,
#[serde(default = "default_sort_trust_asc")]
pub sort_by: String,
#[serde(default = "default_decay_half_life_days")]
pub decay_half_life_days: f32,
#[serde(default = "default_risk_cap")]
pub risk_cap: f32,
}
fn default_scope_file() -> String {
"file".to_string()
}
fn default_min_history() -> u32 {
1
}
fn default_sort_trust_asc() -> String {
"trust_asc".to_string()
}
fn default_decay_half_life_days() -> f32 {
30.0
}
fn default_risk_cap() -> f32 {
3.0
}
#[derive(Clone, Debug, Deserialize)]
pub struct LayersInput {
pub agent_id: String,
#[serde(default)]
pub scope: Option<String>,
#[serde(default = "default_max_layers")]
pub max_layers: u8,
#[serde(default = "default_true")]
pub include_violations: bool,
#[serde(default = "default_min_nodes_per_layer")]
pub min_nodes_per_layer: u32,
#[serde(default)]
pub node_types: Vec<String>,
#[serde(default = "default_naming_strategy")]
pub naming_strategy: String,
#[serde(default)]
pub exclude_tests: bool,
#[serde(default = "default_violation_limit")]
pub violation_limit: usize,
}
fn default_max_layers() -> u8 {
8
}
fn default_min_nodes_per_layer() -> u32 {
2
}
fn default_naming_strategy() -> String {
"auto".to_string()
}
fn default_violation_limit() -> usize {
100
}
#[derive(Clone, Debug, Deserialize)]
pub struct LayerInspectInput {
pub agent_id: String,
pub level: u8,
#[serde(default)]
pub scope: Option<String>,
#[serde(default = "default_true")]
pub include_edges: bool,
#[serde(default = "default_scan_limit")]
pub top_k: usize,
}
#[derive(Clone, Debug, Deserialize)]
pub struct SurgicalContextInput {
pub node_id: String,
pub agent_id: String,
#[serde(default = "default_surgical_max_lines")]
pub max_lines: u32,
#[serde(default = "default_true")]
pub include_callers: bool,
#[serde(default = "default_true")]
pub include_callees: bool,
#[serde(default = "default_true")]
pub include_blast_radius: bool,
#[serde(default = "default_true")]
pub include_trust_score: bool,
#[serde(default = "default_surgical_max_deps")]
pub max_deps: usize,
}
fn default_surgical_max_lines() -> u32 {
200
}
fn default_surgical_max_deps() -> usize {
20
}
#[derive(Clone, Debug, Serialize)]
pub struct SurgicalContextOutput {
pub node_id: String,
pub label: String,
#[serde(rename = "type")]
pub node_type: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub source: Option<SurgicalSourcePeek>,
pub callers: Vec<SurgicalDep>,
pub callees: Vec<SurgicalDep>,
pub blast_radius_forward: usize,
pub blast_radius_backward: usize,
#[serde(skip_serializing_if = "Option::is_none")]
pub trust_score: Option<f32>,
pub source_stale: bool,
pub elapsed_ms: f64,
}
#[derive(Clone, Debug, Serialize)]
pub struct SurgicalSourcePeek {
pub file_path: String,
pub line_start: u32,
pub line_end: u32,
pub content: String,
pub truncated: bool,
pub stale: bool,
}
#[derive(Clone, Debug, Serialize)]
pub struct SurgicalDep {
pub node_id: String,
pub label: String,
#[serde(rename = "type")]
pub node_type: String,
pub relation: String,
pub weight: f32,
}
#[derive(Clone, Debug, Deserialize)]
pub struct ApplyInput {
pub node_id: String,
pub agent_id: String,
pub file_path: String,
pub line_start: u32,
pub line_end: u32,
pub new_content: String,
#[serde(default = "default_true")]
pub fail_on_stale: bool,
#[serde(default = "default_true")]
pub include_predictions: bool,
#[serde(default = "default_apply_predict_k")]
pub predict_top_k: usize,
}
fn default_apply_predict_k() -> usize {
5
}
#[derive(Clone, Debug, Serialize)]
pub struct ApplyOutput {
pub node_id: String,
pub file_path: String,
pub lines_replaced: u32,
pub diff: String,
pub graph_updated: bool,
pub node_count: u32,
pub predictions: Vec<ApplyPrediction>,
pub elapsed_ms: f64,
}
#[derive(Clone, Debug, Serialize)]
pub struct ApplyPrediction {
pub node_id: String,
pub label: String,
pub likelihood: f32,
pub reason: String,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Default)]
#[serde(rename_all = "lowercase")]
pub enum SearchMode {
#[default]
Literal,
Regex,
Semantic,
}
#[derive(Clone, Debug, Deserialize)]
pub struct SearchInput {
pub agent_id: String,
pub query: String,
#[serde(default)]
pub mode: SearchMode,
#[serde(default)]
pub scope: Option<String>,
#[serde(default = "default_search_top_k")]
pub top_k: u32,
#[serde(default)]
pub case_sensitive: bool,
#[serde(default = "default_context_lines")]
pub context_lines: u32,
#[serde(default)]
pub invert: bool,
#[serde(default)]
pub count_only: bool,
#[serde(default)]
pub multiline: bool,
#[serde(default)]
pub auto_ingest: bool,
#[serde(default)]
pub filename_pattern: Option<String>,
#[serde(default)]
pub max_output_chars: Option<usize>,
}
fn default_search_top_k() -> u32 {
50
}
fn default_context_lines() -> u32 {
2
}
#[derive(Clone, Debug, Serialize)]
pub struct SearchOutput {
pub query: String,
pub mode: String,
pub results: Vec<SearchResultEntry>,
pub total_matches: usize,
pub scope_applied: bool,
pub elapsed_ms: f64,
#[serde(skip_serializing_if = "std::ops::Not::not")]
pub auto_ingested: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub match_count: Option<usize>,
#[serde(skip_serializing_if = "Vec::is_empty")]
pub auto_ingested_paths: Vec<String>,
pub truncated: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub inline_summary: Option<String>,
pub proof_state: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub next_suggested_tool: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub next_suggested_target: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub next_step_hint: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub confidence: Option<f32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub why_this_next_step: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub what_is_missing: Option<String>,
}
#[derive(Clone, Debug, Serialize)]
pub struct SearchResultEntry {
pub node_id: String,
pub label: String,
#[serde(rename = "type")]
pub node_type: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub score: Option<f32>,
pub file_path: String,
pub line_number: u32,
pub matched_line: String,
pub context_before: Vec<String>,
pub context_after: Vec<String>,
pub graph_linked: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub heuristic_signals: Option<HeuristicSignals>,
}
#[derive(Clone, Debug, Deserialize)]
pub struct HelpInput {
pub agent_id: String,
#[serde(default)]
pub tool_name: Option<String>,
}
#[derive(Clone, Debug, Serialize)]
pub struct HelpOutput {
pub formatted: String,
pub tool: Option<String>,
pub found: bool,
#[serde(default)]
pub suggestions: Vec<String>,
pub proof_state: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub next_suggested_tool: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub next_suggested_target: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub next_step_hint: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub confidence: Option<f32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub why_this_next_step: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub what_is_missing: Option<String>,
}
#[derive(Clone, Debug, Deserialize)]
pub struct GlobInput {
pub agent_id: String,
pub pattern: String,
#[serde(default)]
pub scope: Option<String>,
#[serde(default = "default_glob_top_k")]
pub top_k: u32,
#[serde(default)]
pub sort: GlobSort,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Default)]
#[serde(rename_all = "lowercase")]
pub enum GlobSort {
#[default]
Path,
Activation,
}
fn default_glob_top_k() -> u32 {
200
}
#[derive(Clone, Debug, Serialize)]
pub struct GlobOutput {
pub pattern: String,
pub files: Vec<GlobFileEntry>,
pub total_matches: usize,
pub scope_applied: bool,
pub elapsed_ms: f64,
pub proof_state: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub next_suggested_tool: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub next_suggested_target: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub next_step_hint: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub confidence: Option<f32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub why_this_next_step: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub what_is_missing: Option<String>,
}
#[derive(Clone, Debug, Serialize)]
pub struct GlobFileEntry {
pub node_id: String,
pub file_path: String,
pub extension: String,
pub line_count: u32,
pub has_connections: bool,
}
#[derive(Clone, Debug, Deserialize)]
pub struct ReportInput {
pub agent_id: String,
#[serde(default)]
pub max_output_chars: Option<usize>,
}
#[derive(Clone, Debug, Serialize)]
pub struct ReportQueryEntry {
pub tool: String,
pub query: String,
pub elapsed_ms: f64,
pub m1nd_answered: bool,
}
#[derive(Clone, Debug, Serialize)]
pub struct ReportHeuristicHotspot {
pub node_id: String,
pub file_path: String,
pub risk_level: String,
pub risk_score: f32,
pub heuristic_signals: HeuristicSignals,
}
#[derive(Clone, Debug, Serialize)]
pub struct ReportOutput {
pub agent_id: String,
pub session_queries: u32,
pub session_elapsed_ms: f64,
pub queries_answered: u32,
pub tokens_saved_session: u64,
pub tokens_saved_global: u64,
pub co2_saved_grams: f64,
pub recent_queries: Vec<ReportQueryEntry>,
pub heuristic_hotspots: Vec<ReportHeuristicHotspot>,
pub markdown_summary: String,
pub truncated: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub inline_summary: Option<String>,
}
#[derive(Clone, Debug, Deserialize)]
pub struct ScanAllInput {
pub agent_id: String,
#[serde(default)]
pub scope: Option<String>,
#[serde(default = "default_severity_min")]
pub severity_min: f32,
#[serde(default = "default_true")]
pub graph_validate: bool,
#[serde(default = "default_scan_limit")]
pub limit_per_pattern: usize,
#[serde(default)]
pub patterns: Vec<String>,
}
#[derive(Clone, Debug, Serialize)]
pub struct ScanAllPatternOutput {
pub pattern: String,
pub findings: Vec<ScanFinding>,
pub files_scanned: usize,
pub total_matches_raw: usize,
pub total_matches_validated: usize,
}
#[derive(Clone, Debug, Serialize)]
pub struct ScanAllOutput {
pub patterns_run: usize,
pub total_findings: usize,
pub by_pattern: Vec<ScanAllPatternOutput>,
pub elapsed_ms: f64,
}
#[derive(Clone, Debug, Deserialize)]
pub struct CrossVerifyInput {
pub agent_id: String,
#[serde(default)]
pub scope: Option<String>,
#[serde(default)]
pub check: Vec<String>,
#[serde(default)]
pub include_dotfiles: bool,
#[serde(default)]
pub dotfile_patterns: Vec<String>,
}
#[derive(Clone, Debug, Deserialize)]
pub struct CoverageSessionInput {
pub agent_id: String,
}
#[derive(Clone, Debug, Deserialize)]
pub struct ExternalReferencesInput {
pub agent_id: String,
#[serde(default)]
pub scope: Option<String>,
}
#[derive(Clone, Debug, Deserialize)]
pub struct FederateAutoInput {
pub agent_id: String,
#[serde(default)]
pub scope: Option<String>,
#[serde(default)]
pub current_repo_name: Option<String>,
#[serde(default = "default_federate_auto_max_repos")]
pub max_repos: usize,
#[serde(default = "default_true")]
pub detect_cross_repo_edges: bool,
#[serde(default)]
pub execute: bool,
}
fn default_federate_auto_max_repos() -> usize {
8
}
#[derive(Clone, Debug, Serialize)]
pub struct FederateAutoCurrentRepo {
pub namespace: String,
pub repo_root: String,
}
#[derive(Clone, Debug, Serialize)]
pub struct FederateAutoRepoCandidate {
pub namespace: String,
pub repo_root: String,
pub marker: Option<String>,
pub confidence: String,
pub source_nodes: Vec<String>,
pub source_files: Vec<String>,
pub evidence_types: Vec<String>,
pub sampled_paths: Vec<String>,
pub suggested_action: String,
}
#[derive(Clone, Debug, Serialize)]
pub struct FederateAutoSkippedPath {
pub external_path: String,
pub reason: String,
pub source_node: Option<String>,
pub file_path: Option<String>,
}
#[derive(Clone, Debug, Serialize)]
pub struct FederateAutoOutput {
pub current_repo: FederateAutoCurrentRepo,
pub discovered_repos: Vec<FederateAutoRepoCandidate>,
pub suggested_repos: Vec<FederateRepo>,
pub skipped_paths: Vec<FederateAutoSkippedPath>,
pub executed: bool,
pub federate_result: Option<FederateOutput>,
pub elapsed_ms: f64,
}
#[derive(Clone, Debug, Deserialize)]
pub struct AuditInput {
pub agent_id: String,
pub path: String,
#[serde(default = "default_audit_profile")]
pub profile: String,
#[serde(default = "default_audit_depth")]
pub depth: String,
#[serde(default = "default_true")]
pub cross_verify: bool,
#[serde(default = "default_true")]
pub include_git: bool,
#[serde(default)]
pub include_config: bool,
#[serde(default = "default_audit_scan_patterns")]
pub scan_patterns: String,
#[serde(default = "default_true")]
pub external_refs: bool,
#[serde(default = "default_audit_report_format")]
pub report_format: String,
#[serde(default)]
pub max_output_chars: Option<usize>,
}
#[derive(Clone, Debug, Deserialize)]
pub struct DaemonStartInput {
pub agent_id: String,
#[serde(default)]
pub watch_paths: Vec<String>,
#[serde(default = "default_daemon_poll_interval_ms")]
pub poll_interval_ms: u64,
}
fn default_daemon_poll_interval_ms() -> u64 {
500
}
#[derive(Clone, Debug, Deserialize)]
pub struct DaemonStopInput {
pub agent_id: String,
}
#[derive(Clone, Debug, Deserialize)]
pub struct DaemonStatusInput {
pub agent_id: String,
}
#[derive(Clone, Debug, Deserialize)]
pub struct DaemonTickInput {
pub agent_id: String,
#[serde(default = "default_daemon_tick_limit")]
pub max_files: usize,
}
fn default_daemon_tick_limit() -> usize {
32
}
#[derive(Clone, Debug, Deserialize)]
pub struct AlertsListInput {
pub agent_id: String,
#[serde(default)]
pub include_acked: bool,
#[serde(default = "default_alert_limit")]
pub limit: usize,
}
fn default_alert_limit() -> usize {
50
}
#[derive(Clone, Debug, Deserialize)]
pub struct AlertsAckInput {
pub agent_id: String,
pub alert_ids: Vec<String>,
}
fn default_audit_profile() -> String {
"auto".to_string()
}
fn default_audit_depth() -> String {
"full".to_string()
}
fn default_audit_scan_patterns() -> String {
"all".to_string()
}
fn default_audit_report_format() -> String {
"markdown".to_string()
}
#[derive(Clone, Debug, Deserialize)]
pub struct PanoramicInput {
pub agent_id: String,
#[serde(default)]
pub scope: Option<String>,
#[serde(default = "default_panoramic_top")]
pub top_n: u32,
}
fn default_panoramic_top() -> u32 {
50
}
#[derive(Clone, Debug, Serialize)]
pub struct PanoramicModule {
pub node_id: String,
pub label: String,
pub file_path: String,
pub blast_forward: u32,
pub blast_backward: u32,
pub centrality: f32,
pub combined_risk: f32,
pub is_critical: bool,
}
#[derive(Clone, Debug, Serialize)]
pub struct PanoramicAlert {
pub node_id: String,
pub label: String,
pub combined_risk: f32,
pub reason: String,
}
#[derive(Clone, Debug, Serialize)]
pub struct PanoramicOutput {
pub modules: Vec<PanoramicModule>,
pub total_modules: usize,
pub critical_alerts: Vec<PanoramicAlert>,
pub scope_applied: bool,
pub elapsed_ms: f64,
}
#[derive(Clone, Debug, Deserialize)]
pub struct SavingsInput {
pub agent_id: String,
}
#[derive(Clone, Debug, Serialize)]
pub struct SavingsSessionRecord {
pub agent_id: String,
pub session_start_ms: u64,
pub queries: u32,
pub tokens_saved: u64,
pub co2_grams: f64,
}
#[derive(Clone, Debug, Serialize)]
pub struct SavingsOutput {
pub session_tokens_saved: u64,
pub global_tokens_saved: u64,
pub global_co2_grams: f64,
pub cost_saved_usd: f64,
pub recent_sessions: Vec<SavingsSessionRecord>,
pub formatted_summary: String,
}
#[derive(Clone, Debug, Deserialize)]
pub struct MetricsInput {
pub agent_id: String,
#[serde(default)]
pub scope: Option<String>,
#[serde(default = "default_metrics_node_types")]
pub node_types: Vec<String>,
#[serde(default = "default_metrics_top_k")]
pub top_k: usize,
#[serde(default = "default_metrics_sort")]
pub sort: String,
}
fn default_metrics_node_types() -> Vec<String> {
vec!["file".to_string()]
}
fn default_metrics_top_k() -> usize {
50
}
fn default_metrics_sort() -> String {
"loc_desc".to_string()
}
#[derive(Clone, Debug, Serialize)]
pub struct MetricsOutput {
pub entries: Vec<MetricsEntry>,
pub summary: MetricsSummary,
pub elapsed_ms: f64,
}
#[derive(Clone, Debug, Serialize)]
pub struct MetricsEntry {
pub node_id: String,
pub label: String,
#[serde(rename = "type")]
pub node_type: String,
pub loc: u32,
pub function_count: u32,
pub struct_count: u32,
pub enum_count: u32,
pub class_count: u32,
pub out_degree: u32,
pub in_degree: u32,
pub pagerank: f32,
pub density: f32,
#[serde(skip_serializing_if = "Option::is_none")]
pub file_path: Option<String>,
}
#[derive(Clone, Debug, Serialize)]
pub struct MetricsSummary {
pub total_files: u32,
pub total_loc: u64,
pub total_functions: u32,
pub total_structs: u32,
pub total_enums: u32,
pub total_classes: u32,
pub avg_loc_per_file: f32,
pub max_loc_file: String,
pub max_loc: u32,
}
#[derive(Clone, Debug, Deserialize)]
pub struct TypeTraceInput {
pub agent_id: String,
pub target: String,
#[serde(default = "default_type_trace_direction")]
pub direction: String,
#[serde(default = "default_type_trace_max_hops")]
pub max_hops: u8,
#[serde(default = "default_metrics_top_k")]
pub top_k: usize,
#[serde(default = "default_true")]
pub group_by_file: bool,
}
fn default_type_trace_direction() -> String {
"forward".to_string()
}
fn default_type_trace_max_hops() -> u8 {
4
}
#[derive(Clone, Debug, Serialize)]
pub struct TypeTraceOutput {
pub target: String,
pub target_label: String,
pub target_type: String,
pub direction: String,
pub max_hops_used: u8,
pub usages: Vec<TypeTraceUsage>,
#[serde(skip_serializing_if = "Vec::is_empty")]
pub by_file: Vec<TypeTraceFileGroup>,
pub total_usages: usize,
pub total_files: usize,
pub elapsed_ms: f64,
}
#[derive(Clone, Debug, Serialize)]
pub struct TypeTraceUsage {
pub node_id: String,
pub label: String,
#[serde(rename = "type")]
pub node_type: String,
pub hops: u8,
pub relation: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub file_path: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub line_start: Option<u32>,
}
#[derive(Clone, Debug, Serialize)]
pub struct TypeTraceFileGroup {
pub file: String,
pub usage_count: usize,
pub usages: Vec<TypeTraceUsage>,
}
#[derive(Clone, Debug, Deserialize)]
pub struct DiagramInput {
pub agent_id: String,
#[serde(default)]
pub center: Option<String>,
#[serde(default)]
pub scope: Option<String>,
#[serde(default = "default_diagram_format")]
pub format: String,
#[serde(default = "default_diagram_max_nodes")]
pub max_nodes: usize,
#[serde(default = "default_diagram_depth")]
pub depth: u8,
#[serde(default)]
pub node_types: Vec<String>,
#[serde(default = "default_true")]
pub show_relations: bool,
#[serde(default)]
pub show_pagerank: bool,
#[serde(default = "default_diagram_direction")]
pub direction: String,
}
fn default_diagram_format() -> String {
"mermaid".to_string()
}
fn default_diagram_max_nodes() -> usize {
30
}
fn default_diagram_depth() -> u8 {
2
}
fn default_diagram_direction() -> String {
"TD".to_string()
}
#[derive(Clone, Debug, Serialize)]
pub struct DiagramOutput {
pub source: String,
pub format: String,
pub node_count: usize,
pub edge_count: usize,
#[serde(skip_serializing_if = "Option::is_none")]
pub center_node: Option<String>,
pub elapsed_ms: f64,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn seek_input_deserializes_minimal() {
let json = r#"{"query": "find auth code", "agent_id": "jimi"}"#;
let input: SeekInput = serde_json::from_str(json).unwrap();
assert_eq!(input.query, "find auth code");
assert_eq!(input.agent_id, "jimi");
assert_eq!(input.top_k, 20);
assert!(input.scope.is_none());
assert!(input.node_types.is_empty());
assert!((input.min_score - 0.1).abs() < f32::EPSILON);
assert!(input.graph_rerank);
}
#[test]
fn scan_input_defaults() {
let json = r#"{"pattern": "error_handling", "agent_id": "jimi"}"#;
let input: ScanInput = serde_json::from_str(json).unwrap();
assert_eq!(input.pattern, "error_handling");
assert!((input.severity_min - 0.3).abs() < f32::EPSILON);
assert!(input.graph_validate);
assert_eq!(input.limit, 50);
}
#[test]
fn timeline_input_deserializes_minimal() {
let json = r#"{"node": "file::backend/config.py", "agent_id": "jimi"}"#;
let input: TimelineInput = serde_json::from_str(json).unwrap();
assert_eq!(input.node, "file::backend/config.py");
assert_eq!(input.depth, "30d");
assert!(input.include_co_changes);
assert!(input.include_churn);
assert_eq!(input.top_k, 10);
}
#[test]
fn diverge_input_with_scope() {
let json = r#"{
"agent_id": "jimi",
"baseline": "2026-03-01",
"scope": "backend/stormender*"
}"#;
let input: DivergeInput = serde_json::from_str(json).unwrap();
assert_eq!(input.baseline, "2026-03-01");
assert_eq!(input.scope.as_deref(), Some("backend/stormender*"));
assert!(input.include_coupling_changes);
assert!(input.include_anomalies);
}
#[test]
fn trail_save_input_minimal() {
let json = r#"{"agent_id": "jimi", "label": "race condition investigation"}"#;
let input: TrailSaveInput = serde_json::from_str(json).unwrap();
assert_eq!(input.label, "race condition investigation");
assert!(input.hypotheses.is_empty());
assert!(input.conclusions.is_empty());
assert!(input.open_questions.is_empty());
assert!(input.tags.is_empty());
}
#[test]
fn trail_resume_input_defaults() {
let json = r#"{"agent_id": "jimi", "trail_id": "trail_jimi_001_abc"}"#;
let input: TrailResumeInput = serde_json::from_str(json).unwrap();
assert_eq!(input.trail_id, "trail_jimi_001_abc");
assert!(!input.force);
assert_eq!(input.max_reactivated_nodes, 5);
assert_eq!(input.max_resume_hints, 4);
}
#[test]
fn trail_merge_input_two_trails() {
let json = r#"{
"agent_id": "jimi",
"trail_ids": ["trail_a", "trail_b"]
}"#;
let input: TrailMergeInput = serde_json::from_str(json).unwrap();
assert_eq!(input.trail_ids.len(), 2);
assert!(input.label.is_none());
}
#[test]
fn trail_list_input_with_filters() {
let json = r#"{
"agent_id": "jimi",
"filter_status": "saved",
"filter_tags": ["stormender", "concurrency"]
}"#;
let input: TrailListInput = serde_json::from_str(json).unwrap();
assert_eq!(input.filter_status.as_deref(), Some("saved"));
assert_eq!(input.filter_tags.len(), 2);
}
#[test]
fn hypothesize_input_minimal() {
let json = r#"{
"claim": "chat_handler never validates session tokens",
"agent_id": "jimi"
}"#;
let input: HypothesizeInput = serde_json::from_str(json).unwrap();
assert_eq!(input.claim, "chat_handler never validates session tokens");
assert_eq!(input.max_hops, 5);
assert!(input.include_ghost_edges);
assert!(input.include_partial_flow);
assert_eq!(input.path_budget, 1000);
}
#[test]
fn differential_input_minimal() {
let json = r#"{
"agent_id": "jimi",
"snapshot_a": "before.json",
"snapshot_b": "current"
}"#;
let input: DifferentialInput = serde_json::from_str(json).unwrap();
assert_eq!(input.snapshot_a, "before.json");
assert_eq!(input.snapshot_b, "current");
assert!(input.question.is_none());
assert!(input.focus_nodes.is_empty());
}
#[test]
fn trace_input_minimal() {
let json = r#"{
"error_text": "Traceback (most recent call last):\n File \"test.py\", line 1\nTypeError: bad",
"agent_id": "jimi"
}"#;
let input: TraceInput = serde_json::from_str(json).unwrap();
assert!(input.language.is_none());
assert!((input.window_hours - 24.0).abs() < f32::EPSILON);
assert_eq!(input.top_k, 10);
}
#[test]
fn validate_plan_input_with_actions() {
let json = r#"{
"agent_id": "jimi",
"actions": [
{"action_type": "modify", "file_path": "backend/config.py"},
{"action_type": "test", "file_path": "backend/tests/test_config.py"}
]
}"#;
let input: ValidatePlanInput = serde_json::from_str(json).unwrap();
assert_eq!(input.actions.len(), 2);
assert!(input.include_test_impact);
assert!(input.include_risk_score);
assert_eq!(input.actions[0].action_type, "modify");
assert!(input.actions[0].depends_on.is_empty());
}
#[test]
fn federate_input_minimal() {
let json = r#"{
"agent_id": "jimi",
"repos": [
{"name": "my-project", "path": "/tmp/my-project"},
{"name": "my-library", "path": "/tmp/my-library"}
]
}"#;
let input: FederateInput = serde_json::from_str(json).unwrap();
assert_eq!(input.repos.len(), 2);
assert!(input.detect_cross_repo_edges);
assert!(!input.incremental);
assert_eq!(input.repos[0].name, "my-project");
assert_eq!(input.repos[1].adapter, "code");
}
#[test]
fn surgical_context_input_minimal() {
let json = r#"{"node_id": "file::backend/chat_handler.py", "agent_id": "jimi"}"#;
let input: SurgicalContextInput = serde_json::from_str(json).unwrap();
assert_eq!(input.node_id, "file::backend/chat_handler.py");
assert_eq!(input.agent_id, "jimi");
assert_eq!(input.max_lines, 200);
assert!(input.include_callers);
assert!(input.include_callees);
assert!(input.include_blast_radius);
assert!(input.include_trust_score);
assert_eq!(input.max_deps, 20);
}
#[test]
fn surgical_context_input_custom_max_lines() {
let json = r#"{
"node_id": "func::handle_chat",
"agent_id": "forge",
"max_lines": 50,
"include_callers": false,
"max_deps": 5
}"#;
let input: SurgicalContextInput = serde_json::from_str(json).unwrap();
assert_eq!(input.max_lines, 50);
assert!(!input.include_callers);
assert!(input.include_callees);
assert_eq!(input.max_deps, 5);
}
#[test]
fn apply_input_minimal() {
let json = r#"{
"node_id": "func::handle_chat",
"agent_id": "forge",
"file_path": "/tmp/project/backend/chat_handler.py",
"line_start": 42,
"line_end": 55,
"new_content": "def handle_chat(request):\n return Response(200)\n"
}"#;
let input: ApplyInput = serde_json::from_str(json).unwrap();
assert_eq!(input.node_id, "func::handle_chat");
assert_eq!(input.line_start, 42);
assert_eq!(input.line_end, 55);
assert!(input.fail_on_stale);
assert!(input.include_predictions);
assert_eq!(input.predict_top_k, 5);
}
#[test]
fn apply_input_no_predictions() {
let json = r#"{
"node_id": "func::process_task",
"agent_id": "forge",
"file_path": "/tmp/project/backend/worker_pool.py",
"line_start": 10,
"line_end": 10,
"new_content": " pass\n",
"include_predictions": false
}"#;
let input: ApplyInput = serde_json::from_str(json).unwrap();
assert!(!input.include_predictions);
assert!(input.fail_on_stale); }
}