@skyhook-io/radar-app 1.13.1 → 1.13.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/package.json +2 -2
  2. package/src/App.tsx +19 -1
  3. package/src/RadarApp.tsx +2 -2
  4. package/src/api/diagnose.test.ts +268 -0
  5. package/src/api/diagnose.ts +72 -9
  6. package/src/components/diagnose/AISettings.tsx +1 -1
  7. package/src/components/diagnose/AgentSetupNotice.tsx +5 -5
  8. package/src/components/diagnose/ApplyDialog.test.tsx +72 -0
  9. package/src/components/diagnose/DiagnoseContext.tsx +12 -17
  10. package/src/components/diagnose/DiagnoseSurface.test.tsx +211 -16
  11. package/src/components/diagnose/DiagnoseSurface.tsx +464 -133
  12. package/src/components/diagnose/Home.test.tsx +293 -0
  13. package/src/components/diagnose/Home.tsx +289 -119
  14. package/src/components/diagnose/InvestigationEvidencePane.test.tsx +2170 -0
  15. package/src/components/diagnose/InvestigationEvidencePane.tsx +2253 -0
  16. package/src/components/diagnose/InvestigationResourceEvidence.test.tsx +257 -0
  17. package/src/components/diagnose/InvestigationResourceEvidence.tsx +214 -0
  18. package/src/components/diagnose/InvestigationView.test.ts +17 -0
  19. package/src/components/diagnose/InvestigationView.tsx +1900 -393
  20. package/src/components/diagnose/LocalDiagnoseAction.tsx +42 -25
  21. package/src/components/diagnose/agentCatalog.ts +1 -1
  22. package/src/components/diagnose/diagnoseEvidenceTypes.ts +151 -0
  23. package/src/components/diagnose/investigationEvidence.test.ts +3109 -0
  24. package/src/components/diagnose/investigationEvidence.ts +3492 -0
  25. package/src/components/diagnose/investigationEvidencePresentation.test.ts +447 -0
  26. package/src/components/diagnose/investigationEvidencePresentation.ts +167 -0
  27. package/src/components/diagnose/investigationExplanation.test.ts +63 -0
  28. package/src/components/diagnose/investigationExplanation.ts +22 -0
  29. package/src/components/diagnose/investigationResourceEvidenceModel.ts +322 -0
  30. package/src/components/diagnose/investigationSourceFocus.test.ts +143 -0
  31. package/src/components/diagnose/investigationSourceFocus.ts +98 -0
  32. package/src/components/diagnose/investigationState.test.ts +695 -0
  33. package/src/components/diagnose/investigationState.ts +451 -0
  34. package/src/components/diagnose/parts.test.tsx +864 -3
  35. package/src/components/diagnose/parts.tsx +1337 -541
  36. package/src/components/diagnose/target.test.ts +39 -0
  37. package/src/components/diagnose/target.ts +36 -0
  38. package/src/components/diagnose/useDisclosureReveal.ts +117 -0
  39. package/src/components/home/MCPSetupDialog.tsx +2 -2
  40. package/src/components/home/mcpToolCatalog.test.ts +22 -0
  41. package/src/components/home/mcpToolCatalog.ts +3 -2
  42. package/src/components/issues/IssuesPane.tsx +5 -1
  43. package/src/components/settings/SettingsDialog.tsx +11 -13
  44. package/src/components/workload/WorkloadView.tsx +1 -1
  45. package/src/context/DiagnoseCustomization.tsx +11 -8
  46. package/src/index.css +63 -79
  47. package/src/index.ts +1 -1
@@ -1,9 +1,6 @@
1
1
  import { Loader2, Sparkles } from "lucide-react";
2
- import {
3
- useDiagnose,
4
- useDiagnoseLayout,
5
- runTargetKey,
6
- } from "./DiagnoseContext";
2
+ import { useDiagnose, useDiagnoseLayout } from "./DiagnoseContext";
3
+ import { runTargetKey } from "./target";
7
4
  import { Tooltip } from "../ui/Tooltip";
8
5
  import type { RenderDiagnoseAction } from "../../context/DiagnoseCustomization";
9
6
 
@@ -13,18 +10,20 @@ import type { RenderDiagnoseAction } from "../../context/DiagnoseCustomization";
13
10
  // this slot with their own action.
14
11
  //
15
12
  // Adaptive by health: on a resource with a live problem it reads as a prominent
16
- // "Diagnose" (find the root cause); when the resource is fine or health is unknown
17
- // it shrinks to a quiet colored-icon affordance ("ask my agent about this") — so it
18
- // never implies "something is wrong here" on a healthy resource. The tooltip leads
19
- // with the BYO framing (the user's OWN agent, locally) — unless the agent is
20
- // hosted, where those claims would be false.
13
+ // "Investigate" action; when the resource is fine or health is unknown it shrinks
14
+ // to a quiet colored-icon affordance ("ask my agent about this") — so it never
15
+ // implies "something is wrong here" on a healthy resource. The tooltip leads with
16
+ // the BYO framing (the user's OWN agent, locally) — unless the agent is hosted,
17
+ // where those claims would be false.
21
18
  function DiagnoseResourceButton({
22
19
  kind,
20
+ group,
23
21
  namespace,
24
22
  name,
25
23
  health,
26
24
  }: {
27
25
  kind: string;
26
+ group?: string;
28
27
  namespace: string;
29
28
  name: string;
30
29
  health?: "problem" | "healthy" | "unknown";
@@ -37,17 +36,18 @@ function DiagnoseResourceButton({
37
36
  if (d.setupState === "off") return null;
38
37
  const ready = d.available;
39
38
  const problem = health === "problem";
40
- const running = ready && runningKeys.has(runTargetKey(kind, namespace, name));
39
+ const running =
40
+ ready && runningKeys.has(runTargetKey(kind, namespace, name, group ?? ""));
41
41
  const tooltip = !ready
42
- ? "Set up AI diagnosis to investigate this — runs your own agent locally."
42
+ ? "Set up AI investigations — runs your own agent locally."
43
43
  : running
44
44
  ? `${d.agentLabel} is investigating this resource — click to watch it live.`
45
45
  : d.hosted
46
46
  ? problem
47
- ? `Diagnose with ${d.agentLabel} — reads this resource's spec, events & logs to find the root cause.`
47
+ ? `Investigate with ${d.agentLabel} — reads this resource's spec, events & logs to look for the cause.`
48
48
  : `Ask ${d.agentLabel} about this resource — reads its spec, events & logs.`
49
49
  : problem
50
- ? `Diagnose with your own ${d.agentLabel} — runs locally, reads this resource's spec, events & logs to find the root cause.`
50
+ ? `Investigate with your own ${d.agentLabel} — runs locally and reads this resource's spec, events & logs.`
51
51
  : `Ask your own ${d.agentLabel} about this resource — runs locally, reads its spec, events & logs.`;
52
52
  // While an investigation is live, the button advertises it (and clicking focuses
53
53
  // the existing run rather than starting a new one — openInvestigation dedups).
@@ -56,15 +56,22 @@ function DiagnoseResourceButton({
56
56
  <Tooltip content={tooltip} position="bottom">
57
57
  <button
58
58
  onClick={() =>
59
- ready ? d.openInvestigation({ kind, namespace, name }) : d.openHome()
59
+ ready
60
+ ? d.openInvestigation({
61
+ kind,
62
+ group: group ?? "",
63
+ namespace,
64
+ name,
65
+ })
66
+ : d.openHome()
60
67
  }
61
68
  aria-label={
62
69
  !ready
63
- ? "Set up AI diagnosis"
70
+ ? "Set up AI investigations"
64
71
  : running
65
72
  ? "Investigation running — click to view"
66
73
  : problem
67
- ? "Diagnose with AI"
74
+ ? "Investigate with AI"
68
75
  : "Ask AI about this resource"
69
76
  }
70
77
  className={
@@ -78,7 +85,7 @@ function DiagnoseResourceButton({
78
85
  ) : (
79
86
  <Sparkles className="h-3.5 w-3.5 text-accent" />
80
87
  )}
81
- {running ? "Investigating…" : problem && "Diagnose"}
88
+ {running ? "Investigating…" : problem && "Investigate"}
82
89
  </button>
83
90
  </Tooltip>
84
91
  );
@@ -86,27 +93,31 @@ function DiagnoseResourceButton({
86
93
 
87
94
  export const defaultDiagnoseAction: RenderDiagnoseAction = ({
88
95
  kind,
96
+ group,
89
97
  namespace,
90
98
  name,
91
99
  health,
92
100
  }) => (
93
101
  <DiagnoseResourceButton
94
102
  kind={kind}
103
+ group={group}
95
104
  namespace={namespace}
96
105
  name={name}
97
106
  health={health}
98
107
  />
99
108
  );
100
109
 
101
- // Compact per-issue "Diagnose" action for the Issues queue — launches an
110
+ // Compact per-issue "Investigate" action for the Issues queue — launches an
102
111
  // investigation for the issue's subject from where the problem is surfaced.
103
112
  // stopPropagation so it doesn't toggle the issue row it lives in.
104
113
  export function IssueDiagnoseButton({
105
114
  kind,
115
+ group,
106
116
  namespace,
107
117
  name,
108
118
  }: {
109
119
  kind: string;
120
+ group?: string;
110
121
  namespace: string;
111
122
  name: string;
112
123
  }) {
@@ -117,25 +128,31 @@ export function IssueDiagnoseButton({
117
128
  <Tooltip
118
129
  content={
119
130
  d.setupState === "needs-restart"
120
- ? "Restart Radar to enable AI diagnosis — a supported agent is installed"
131
+ ? "Restart Radar to enable AI investigations — a supported agent is installed"
121
132
  : !ready
122
- ? "Set up AI diagnosis — install a local agent to investigate"
133
+ ? "Set up AI investigations — install a local agent"
123
134
  : d.hosted
124
- ? `Sends this resource's context to ${d.agentLabel} to find the root cause`
125
- : `Runs ${d.agentLabel} on your machine and sends it this resource's context to find the root cause`
135
+ ? `Sends this resource's context to ${d.agentLabel} for investigation`
136
+ : `Runs ${d.agentLabel} on your machine to investigate this resource`
126
137
  }
127
138
  position="left"
128
139
  >
129
140
  <button
130
141
  onClick={(e) => {
131
142
  e.stopPropagation();
132
- if (ready) d.openInvestigation({ kind, namespace, name });
143
+ if (ready)
144
+ d.openInvestigation({
145
+ kind,
146
+ group: group ?? "",
147
+ namespace,
148
+ name,
149
+ });
133
150
  else d.openHome();
134
151
  }}
135
152
  className="flex shrink-0 items-center gap-1 rounded-md border border-theme-border px-2 py-1 text-xs text-theme-text-secondary hover:bg-theme-hover hover:text-theme-text-primary"
136
153
  >
137
154
  <Sparkles className="h-3 w-3 text-accent" />
138
- Diagnose
155
+ Investigate
139
156
  </button>
140
157
  </Tooltip>
141
158
  );
@@ -1,5 +1,5 @@
1
1
  // Install metadata for the agent CLIs Radar can drive. Used by the setup notice
2
- // shown when AI diagnosis is eligible but no agent is installed. Names match the
2
+ // shown when AI investigations are eligible but no agent is installed. Names match the
3
3
  // backend's supported set (see internal/ai/detect.go knownAgents + isSupportedAgent).
4
4
  export interface AgentInstall {
5
5
  name: string; // stable id, matches AgentInfo.name
@@ -0,0 +1,151 @@
1
+ /**
2
+ * Structural contracts emitted by Radar evidence tools and consumed by the
3
+ * investigation projection. These deliberately exclude any presentation or
4
+ * transport envelope so the live Diagnose UI is not coupled to another route.
5
+ */
6
+ export interface DiagnosisFilteredLogs {
7
+ lines: string[] | null;
8
+ totalLines: number;
9
+ matchedLines: number;
10
+ fallback: boolean;
11
+ }
12
+
13
+ export interface DiagnosisPodLogEntry {
14
+ pod: string;
15
+ container: string;
16
+ logs?: DiagnosisFilteredLogs;
17
+ error?: string;
18
+ }
19
+
20
+ export interface DiagnosisPodContainerRef {
21
+ pod: string;
22
+ container: string;
23
+ }
24
+
25
+ export interface DiagnosisCrashCause {
26
+ pods: string[];
27
+ container: string;
28
+ state: string;
29
+ reason?: string;
30
+ exitCode: number;
31
+ logLine: string;
32
+ logSource: string;
33
+ logLineSelection:
34
+ | "fatal_pattern"
35
+ | "traceback_header_only"
36
+ | "last_matched_line"
37
+ | "log_tail"
38
+ | string;
39
+ }
40
+
41
+ export interface DiagnosisStartupBlocker {
42
+ kind: string;
43
+ name: string;
44
+ reason: string;
45
+ severity: string;
46
+ message: string;
47
+ }
48
+
49
+ export interface DiagnosisResourceRef {
50
+ kind: string;
51
+ group?: string;
52
+ namespace?: string;
53
+ name: string;
54
+ }
55
+
56
+ export interface DiagnosisResourceContext {
57
+ tier: "basic" | "diagnostic";
58
+ owner?: DiagnosisResourceRef;
59
+ managedBy?: DiagnosisResourceRef[];
60
+ exposes?: DiagnosisResourceRef[];
61
+ selectedBy?: DiagnosisResourceRef[];
62
+ referencedBy?: {
63
+ total: number;
64
+ items?: Array<DiagnosisResourceRef & { paths?: string[] }>;
65
+ truncated?: boolean;
66
+ };
67
+ uses?: {
68
+ configMaps?: DiagnosisResourceRef[];
69
+ secrets?: DiagnosisResourceRef[];
70
+ serviceAccount?: DiagnosisResourceRef;
71
+ pvcs?: DiagnosisResourceRef[];
72
+ };
73
+ runsOn?: DiagnosisResourceRef;
74
+ scaledBy?: DiagnosisResourceRef[];
75
+ statusSummary?: {
76
+ phase?: string;
77
+ conditions?: Array<{
78
+ type: string;
79
+ status: string;
80
+ reason?: string;
81
+ message?: string;
82
+ lastTransitionTime?: string;
83
+ }>;
84
+ };
85
+ workloadSummary?: {
86
+ replicas?: {
87
+ desired?: number;
88
+ ready?: number;
89
+ available?: number;
90
+ updated?: number;
91
+ unavailable?: number;
92
+ };
93
+ rolloutRisk?: {
94
+ reason: string;
95
+ replicas: number;
96
+ maxSurge: string;
97
+ maxUnavailable: string;
98
+ resolvedMaxSurge: number;
99
+ resolvedMaxUnavailable: number;
100
+ message: string;
101
+ action: string;
102
+ };
103
+ };
104
+ appReferences?: {
105
+ staleSecretEnvTruncated?: boolean;
106
+ };
107
+ omitted?: Array<{
108
+ field: string;
109
+ reason: string;
110
+ }>;
111
+ }
112
+
113
+ export interface DiagnosisChangeContext {
114
+ changed: boolean;
115
+ what?: string;
116
+ when?: string;
117
+ evidence?: string;
118
+ }
119
+
120
+ export interface DiagnosisDNSContext {
121
+ signals?: string[];
122
+ coreDNSFindings?: Array<{
123
+ kind: string;
124
+ namespace: string;
125
+ name: string;
126
+ severity: string;
127
+ reason: string;
128
+ message?: string;
129
+ }>;
130
+ }
131
+
132
+ export type DiagnosisEvidenceTone =
133
+ "error" | "alert" | "warning" | "info" | "neutral";
134
+
135
+ export interface DiagnosisEvidenceLimitationBase {
136
+ source: string;
137
+ message: string;
138
+ kind: "error" | "truncated" | "unknown";
139
+ }
140
+
141
+ export function diagnosisSeverityTone(severity: string): DiagnosisEvidenceTone {
142
+ const value = severity.toLowerCase();
143
+ if (value === "critical" || value === "error" || value === "failed")
144
+ return "error";
145
+ if (value === "high" || value === "alert") return "alert";
146
+ if (value === "medium" || value === "warning" || value === "warn")
147
+ return "warning";
148
+ if (value === "low" || value === "info" || value === "informational")
149
+ return "info";
150
+ return "neutral";
151
+ }