@skyhook-io/radar-app 1.13.3 → 1.13.5

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 (45) hide show
  1. package/package.json +6 -6
  2. package/src/App.tsx +68 -23
  3. package/src/RadarApp.tsx +17 -1
  4. package/src/api/client.ts +28 -10
  5. package/src/api/diagnose.ts +61 -15
  6. package/src/components/ConnectionErrorView.test.tsx +30 -0
  7. package/src/components/ConnectionErrorView.tsx +31 -19
  8. package/src/components/diagnose/AgentCase.tsx +131 -0
  9. package/src/components/diagnose/DiagnoseContext.test.ts +95 -0
  10. package/src/components/diagnose/DiagnoseContext.tsx +402 -77
  11. package/src/components/diagnose/DiagnoseSurface.test.tsx +53 -54
  12. package/src/components/diagnose/DiagnoseSurface.tsx +198 -286
  13. package/src/components/diagnose/Home.test.tsx +23 -1
  14. package/src/components/diagnose/Home.tsx +49 -3
  15. package/src/components/diagnose/InvestigationEvidencePane.test.tsx +1446 -5
  16. package/src/components/diagnose/InvestigationEvidencePane.tsx +1373 -126
  17. package/src/components/diagnose/InvestigationView.tsx +227 -5
  18. package/src/components/diagnose/LocalDiagnoseAction.tsx +2 -3
  19. package/src/components/diagnose/diagnoseEvidenceTypes.ts +22 -1
  20. package/src/components/diagnose/investigationCase.test.tsx +1568 -0
  21. package/src/components/diagnose/investigationCase.ts +439 -0
  22. package/src/components/diagnose/investigationEvidence.test.ts +3525 -17
  23. package/src/components/diagnose/investigationEvidence.ts +2246 -166
  24. package/src/components/diagnose/investigationEvidenceKinds.ts +218 -0
  25. package/src/components/diagnose/investigationEvidencePresentation.test.ts +31 -0
  26. package/src/components/diagnose/investigationEvidencePresentation.ts +1 -0
  27. package/src/components/diagnose/investigationMetrics.test.ts +712 -0
  28. package/src/components/diagnose/investigationMetrics.ts +393 -0
  29. package/src/components/diagnose/investigationSourceFocus.ts +6 -0
  30. package/src/components/diagnose/investigationState.test.ts +322 -0
  31. package/src/components/diagnose/investigationState.ts +147 -25
  32. package/src/components/diagnose/parts.test.tsx +537 -1
  33. package/src/components/diagnose/parts.tsx +486 -42
  34. package/src/components/resource/HPACharts.render.test.tsx +77 -0
  35. package/src/components/resource/HPACharts.tsx +32 -25
  36. package/src/components/resource/PrometheusChartsGrid.tsx +181 -79
  37. package/src/components/resources/PodFilePreview.test.tsx +131 -0
  38. package/src/components/resources/PodFilePreview.tsx +394 -0
  39. package/src/components/resources/PodFilesystemModal.tsx +157 -67
  40. package/src/components/resources/ResourcesView.tsx +27 -2
  41. package/src/context/DiagnoseCustomization.test.tsx +26 -0
  42. package/src/context/DiagnoseCustomization.tsx +14 -2
  43. package/src/index.ts +8 -5
  44. package/src/utils/shell-safe.test.ts +25 -1
  45. package/src/utils/shell-safe.ts +16 -0
@@ -1,15 +1,13 @@
1
1
  import { describe, expect, it, vi } from "vitest";
2
2
  import {
3
3
  DIAGNOSE_SURFACE_FRAME_CLASS,
4
- MAXIMIZED_COMPACT_HISTORY_VISIBILITY_CLASS,
5
- MAXIMIZED_HOME_DETAIL_VISIBILITY_CLASS,
6
- MAXIMIZED_HOME_RUN_HEADER_VISIBILITY_CLASS,
7
4
  INVESTIGATION_HISTORY_MIN_WIDTH,
8
5
  MAXIMIZED_RUN_META_VISIBILITY_CLASS,
9
- canStartNewInvestigation,
10
- canCopyRunLink,
6
+ canRerunInvestigation,
7
+ investigationHistoryIsPersistent,
11
8
  investigationHeaderPresentation,
12
9
  openInvestigationEvidenceResource,
10
+ unavailableInvestigationMessage,
13
11
  } from "./DiagnoseSurface";
14
12
  import {
15
13
  canContinueInvestigation,
@@ -18,7 +16,7 @@ import {
18
16
  } from "./investigationState";
19
17
  import type { RunSummary } from "../../api/diagnose";
20
18
 
21
- // The "new investigation" button dispatches an agent and spends the user's own
19
+ // The re-run button dispatches an agent and spends the user's own
22
20
  // tokens, so every one of these clauses is load-bearing rather than cosmetic.
23
21
  // Each case below is a way it misfired before the gate existed.
24
22
  function run(status: RunSummary["status"]): RunSummary {
@@ -35,41 +33,40 @@ function run(status: RunSummary["status"]): RunSummary {
35
33
  };
36
34
  }
37
35
 
38
- describe("canStartNewInvestigation", () => {
36
+ describe("canRerunInvestigation", () => {
39
37
  it("offers a new investigation on a finished run", () => {
40
- expect(canStartNewInvestigation("investigation", run("done"), false)).toBe(
38
+ expect(canRerunInvestigation("investigation", run("done"), false)).toBe(
41
39
  true,
42
40
  );
43
41
  });
44
42
 
45
43
  it("stays hidden on the investigations list", () => {
46
- // goHome() leaves activeRunId set, so the header still has a run to read.
47
- // Without the view check the click starts an agent on a resource the user
48
- // navigated away from, over a list of unrelated investigations.
49
- expect(canStartNewInvestigation("home", run("done"), false)).toBe(false);
44
+ // Home is a fresh-entry surface and must never expose a token-spending
45
+ // re-run action, even if a stale caller hands the header a run.
46
+ expect(canRerunInvestigation("home", run("done"), false)).toBe(false);
50
47
  });
51
48
 
52
49
  it("stays hidden while a turn is in flight", () => {
53
50
  // A start would be handed back the live run, so the button does nothing.
54
51
  expect(
55
- canStartNewInvestigation("investigation", run("running"), false),
52
+ canRerunInvestigation("investigation", run("running"), false),
56
53
  ).toBe(false);
57
54
  });
58
55
 
59
56
  it("stays hidden on a stale run", () => {
60
57
  // A closed session must not start a new investigation against a resource
61
58
  // that may not exist in the active cluster.
62
- expect(canStartNewInvestigation("investigation", run("stale"), false)).toBe(
59
+ expect(canRerunInvestigation("investigation", run("stale"), false)).toBe(
63
60
  false,
64
61
  );
65
62
  });
66
63
 
67
64
  it("blocks fresh starts while a human turn stops, but allows a separate human run from an automatic one", () => {
68
65
  expect(
69
- canStartNewInvestigation("investigation", run("stopping"), false),
66
+ canRerunInvestigation("investigation", run("stopping"), false),
70
67
  ).toBe(false);
71
68
  expect(
72
- canStartNewInvestigation(
69
+ canRerunInvestigation(
73
70
  "investigation",
74
71
  { ...run("running"), trigger: "background" },
75
72
  false,
@@ -78,27 +75,39 @@ describe("canStartNewInvestigation", () => {
78
75
  });
79
76
 
80
77
  it("stays hidden while consent is pending", () => {
81
- expect(canStartNewInvestigation("investigation", run("done"), true)).toBe(
78
+ expect(canRerunInvestigation("investigation", run("done"), true)).toBe(
82
79
  false,
83
80
  );
84
81
  });
85
82
 
86
83
  it("stays hidden with no focused run", () => {
87
- expect(canStartNewInvestigation("investigation", null, false)).toBe(false);
84
+ expect(canRerunInvestigation("investigation", null, false)).toBe(false);
88
85
  });
89
86
 
90
87
  it("offers one on an errored or stopped run", () => {
91
88
  // Those are the runs a person most wants to start over from.
92
- expect(canStartNewInvestigation("investigation", run("error"), false)).toBe(
89
+ expect(canRerunInvestigation("investigation", run("error"), false)).toBe(
93
90
  true,
94
91
  );
95
92
  expect(
96
- canStartNewInvestigation("investigation", run("stopped"), false),
93
+ canRerunInvestigation("investigation", run("stopped"), false),
97
94
  ).toBe(true);
98
95
  });
99
96
  });
100
97
 
101
98
  describe("investigation history navigation", () => {
99
+ it("keeps hosted recovery language out of standalone Radar", () => {
100
+ expect(unavailableInvestigationMessage(false)).toBe(
101
+ "This investigation is unavailable. It may have been removed, or its saved history may have been cleared.",
102
+ );
103
+ expect(unavailableInvestigationMessage(false)).not.toMatch(
104
+ /account|organization|creator|access/,
105
+ );
106
+ expect(unavailableInvestigationMessage(true)).toMatch(
107
+ /account and organization/,
108
+ );
109
+ });
110
+
102
111
  it("restores the docked surface before opening an evidence resource", () => {
103
112
  const events: string[] = [];
104
113
  const onOpenResource = vi.fn(() => events.push("open"));
@@ -117,12 +126,13 @@ describe("investigation history navigation", () => {
117
126
  setMaximized,
118
127
  closeDiagnose,
119
128
  false,
129
+ "run-1",
120
130
  );
121
131
 
122
132
  expect(events).toEqual(["dock", "open"]);
123
133
  expect(setMaximized).toHaveBeenCalledWith(false);
124
134
  expect(closeDiagnose).not.toHaveBeenCalled();
125
- expect(onOpenResource).toHaveBeenCalledWith(ref);
135
+ expect(onOpenResource).toHaveBeenCalledWith(ref, "run-1");
126
136
  });
127
137
 
128
138
  it("closes an overlay before opening an evidence resource", () => {
@@ -147,7 +157,7 @@ describe("investigation history navigation", () => {
147
157
  expect(events).toEqual(["close", "open"]);
148
158
  expect(setMaximized).not.toHaveBeenCalled();
149
159
  expect(closeDiagnose).toHaveBeenCalledOnce();
150
- expect(onOpenResource).toHaveBeenCalledWith(ref);
160
+ expect(onOpenResource).toHaveBeenCalledWith(ref, null);
151
161
  });
152
162
 
153
163
  it("keeps document overflow out of the bounded Diagnose frame", () => {
@@ -159,20 +169,28 @@ describe("investigation history navigation", () => {
159
169
 
160
170
  it("reserves the history rail for wider investigation surfaces", () => {
161
171
  expect(INVESTIGATION_HISTORY_MIN_WIDTH).toBe(1750);
162
- expect(MAXIMIZED_COMPACT_HISTORY_VISIBILITY_CLASS).toBe(
163
- "@min-[1750px]/diagnose-surface:hidden",
164
- );
165
- expect(MAXIMIZED_HOME_DETAIL_VISIBILITY_CLASS).toBe(
166
- "hidden @min-[1750px]/diagnose-surface:flex",
167
- );
168
- expect(MAXIMIZED_HOME_RUN_HEADER_VISIBILITY_CLASS).toBe(
169
- "hidden @min-[1750px]/diagnose-surface:block",
170
- );
171
172
  expect(MAXIMIZED_RUN_META_VISIBILITY_CLASS).toBe(
172
173
  "hidden @min-[1750px]/diagnose-surface:flex",
173
174
  );
174
175
  });
175
176
 
177
+ it("keeps history visible on Home even when the workspace is compact", () => {
178
+ expect(
179
+ investigationHistoryIsPersistent({
180
+ maximized: true,
181
+ view: "home",
182
+ surfaceWidth: 700,
183
+ }),
184
+ ).toBe(true);
185
+ expect(
186
+ investigationHistoryIsPersistent({
187
+ maximized: true,
188
+ view: "investigation",
189
+ surfaceWidth: 700,
190
+ }),
191
+ ).toBe(false);
192
+ });
193
+
176
194
  it("keeps docked Home generic and removes actions for its retained run", () => {
177
195
  expect(
178
196
  investigationHeaderPresentation({
@@ -187,7 +205,7 @@ describe("investigation history navigation", () => {
187
205
  });
188
206
  });
189
207
 
190
- it("swaps generic Home identity for the labeled retained detail at the wide breakpoint", () => {
208
+ it("keeps Home generic even if a stale caller supplies retained detail", () => {
191
209
  expect(
192
210
  investigationHeaderPresentation({
193
211
  view: "home",
@@ -195,9 +213,9 @@ describe("investigation history navigation", () => {
195
213
  hasVisibleRunDetail: true,
196
214
  }),
197
215
  ).toEqual({
198
- genericIdentityClass: MAXIMIZED_COMPACT_HISTORY_VISIBILITY_CLASS,
199
- detailIdentityClass: MAXIMIZED_HOME_RUN_HEADER_VISIBILITY_CLASS,
200
- runActionsClass: MAXIMIZED_HOME_DETAIL_VISIBILITY_CLASS,
216
+ genericIdentityClass: "",
217
+ detailIdentityClass: null,
218
+ runActionsClass: null,
201
219
  });
202
220
  });
203
221
 
@@ -318,25 +336,6 @@ describe("canContinueInvestigation", () => {
318
336
  });
319
337
  });
320
338
 
321
- describe("canCopyRunLink", () => {
322
- it("does not expose collaboration UI for an OSS run", () => {
323
- expect(canCopyRunLink(run("done"))).toBe(false);
324
- });
325
-
326
- it("exposes the copy action only for a canonical hosted URL", () => {
327
- expect(
328
- canCopyRunLink({
329
- ...run("done"),
330
- radarUrl: "/c/cluster-1?org=org-1&ai-run=r1",
331
- }),
332
- ).toBe(true);
333
- });
334
-
335
- it("treats an empty hosted URL as unavailable", () => {
336
- expect(canCopyRunLink({ ...run("done"), radarUrl: "" })).toBe(false);
337
- });
338
- });
339
-
340
339
  describe("canInvestigateFurther", () => {
341
340
  it("offers a context-preserving next step on a completed automatic investigation", () => {
342
341
  expect(