hematite/agent/
diagnose.rs1pub fn triage_follow_up_topics(health_output: &str) -> Vec<&'static str> {
12 let lower = health_output.to_ascii_lowercase();
13 let mut topics: Vec<&'static str> = Vec::new();
14
15 let action_required = lower.contains("action required");
16 let worth_a_look = lower.contains("worth a look");
17 if !action_required && !worth_a_look {
18 return topics; }
20
21 if lower.contains("[!]") && (lower.contains("disk") || lower.contains("drive")) {
23 topics.push("storage");
24 topics.push("disk_health");
25 } else if lower.contains("[-]") && (lower.contains("disk") || lower.contains("drive")) {
26 topics.push("storage");
27 }
28
29 if (lower.contains("[!]") || lower.contains("[-]")) && lower.contains("ram") {
31 topics.push("resource_load");
32 topics.push("processes");
33 }
34
35 if lower.contains("critical") || lower.contains("error event") {
37 if lower.contains("event") {
38 topics.push("log_check");
39 }
40 }
41
42 if lower.contains("[!]") && lower.contains("service") {
44 topics.push("services");
45 } else if lower.contains("[-]") && lower.contains("service") {
46 topics.push("services");
47 }
48
49 if (lower.contains("[!]") || lower.contains("[-]"))
51 && (lower.contains("defender") || lower.contains("firewall") || lower.contains("security"))
52 {
53 topics.push("security");
54 }
55
56 if lower.contains("[!]") && lower.contains("internet connectivity") {
58 topics.push("connectivity");
59 topics.push("network");
60 } else if lower.contains("[-]") && lower.contains("internet connectivity") {
61 topics.push("connectivity");
62 }
63
64 if lower.contains("pending reboot") {
66 topics.push("pending_reboot");
67 }
68
69 if (lower.contains("[!]") || lower.contains("[-]"))
71 && (lower.contains("thermal") || lower.contains("°c"))
72 {
73 topics.push("thermal");
74 topics.push("overclocker");
75 }
76
77 let mut seen = std::collections::HashSet::new();
79 topics.retain(|t| seen.insert(*t));
80
81 topics
82}
83
84pub fn build_diagnose_instruction(health_output: &str, follow_up_topics: &[&str]) -> String {
88 if follow_up_topics.is_empty() {
89 return format!(
90 "DIAGNOSE MODE — triage complete.\n\n\
91 Health report:\n{}\n\n\
92 The machine is in good health. Summarize the key findings for the user \
93 in 2-3 sentences and confirm no action is needed.",
94 health_output
95 );
96 }
97
98 let topic_list = follow_up_topics
99 .iter()
100 .enumerate()
101 .map(|(i, t)| format!("{}. inspect_host(topic=\"{}\")", i + 1, t))
102 .collect::<Vec<_>>()
103 .join("\n");
104
105 format!(
106 "DIAGNOSE MODE — harness triage identified {} area(s) to investigate.\n\n\
107 Health report (already run by harness):\n{}\n\n\
108 PROTOCOL — follow this exactly:\n\
109 Call each topic below in order:\n{}\n\n\
110 After all calls complete:\n\
111 - Write a numbered fix plan grounded in the tool output\n\
112 - Lead with the most critical issue first\n\
113 - Every step must reference specific data from the results (exact path, count, service name, etc.)\n\
114 - No generic advice — only steps that address what the tools actually found\n\
115 - If a finding needs a restart or elevated privileges, say so explicitly",
116 follow_up_topics.len(),
117 health_output,
118 topic_list
119 )
120}