@grackle-ai/web-components 0.115.1 → 0.116.0

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 (55) hide show
  1. package/.rush/temp/chunked-rush-logs/web-components._phase_build.chunks.jsonl +6 -6
  2. package/.rush/temp/chunked-rush-logs/web-components._phase_test.chunks.jsonl +26 -28
  3. package/.rush/temp/{689afee5625b24607e65ba3a6d3b279d5895784d.tar.log → f238c174d295031bec7f186733732e0fd7e4b9a5.tar.log} +55 -60
  4. package/.rush/temp/{689afee5625b24607e65ba3a6d3b279d5895784d.untar.log → f238c174d295031bec7f186733732e0fd7e4b9a5.untar.log} +2 -2
  5. package/.rush/temp/{e12a67384bc67b4cba24253105ba33ed0945c91d.tar.log → f5301e6a84109dcec06242d178df01e555a83456.tar.log} +2 -2
  6. package/.rush/temp/{e12a67384bc67b4cba24253105ba33ed0945c91d.untar.log → f5301e6a84109dcec06242d178df01e555a83456.untar.log} +2 -2
  7. package/.rush/temp/operation/_phase_build/all.log +6 -6
  8. package/.rush/temp/operation/_phase_build/log-chunks.jsonl +6 -6
  9. package/.rush/temp/operation/_phase_build/state.json +1 -1
  10. package/.rush/temp/operation/_phase_test/all.log +26 -28
  11. package/.rush/temp/operation/_phase_test/log-chunks.jsonl +26 -28
  12. package/.rush/temp/operation/_phase_test/state.json +1 -1
  13. package/.rush/temp/shrinkwrap-deps.json +3 -3
  14. package/README.md +2 -2
  15. package/dist/index.css +1 -1
  16. package/dist/index.js +9055 -9498
  17. package/package.json +2 -2
  18. package/rush-logs/web-components._phase_build.cache.log +1 -1
  19. package/rush-logs/web-components._phase_build.log +6 -6
  20. package/rush-logs/web-components._phase_test.cache.log +1 -1
  21. package/rush-logs/web-components._phase_test.log +26 -28
  22. package/src/components/index.ts +0 -3
  23. package/src/components/knowledge/KnowledgeDetailPanel.tsx +1 -4
  24. package/src/components/layout/AppNav.stories.tsx +3 -6
  25. package/src/components/layout/AppNav.tsx +3 -7
  26. package/src/components/layout/BottomStatusBar.tsx +4 -6
  27. package/src/components/lists/index.ts +0 -1
  28. package/src/components/panels/KeyboardShortcutsPanel.tsx +0 -1
  29. package/src/components/panels/index.ts +0 -1
  30. package/src/components/personas/McpToolSelector.stories.tsx +12 -12
  31. package/src/components/tools/ToolCard.stories.tsx +0 -26
  32. package/src/components/tools/ToolCard.tsx +0 -3
  33. package/src/components/tools/ToolSearchCard.stories.tsx +8 -8
  34. package/src/components/tools/WorkpadCard.stories.tsx +5 -5
  35. package/src/components/tools/classifyTool.test.ts +0 -1
  36. package/src/components/tools/classifyTool.ts +2 -7
  37. package/src/context/GrackleContextTypes.ts +1 -3
  38. package/src/hooks/types.ts +1 -44
  39. package/src/index.ts +4 -8
  40. package/src/mocks/MockGrackleProvider.tsx +0 -75
  41. package/src/mocks/mockData.ts +8 -99
  42. package/src/test-utils/storybook-helpers.ts +0 -19
  43. package/src/utils/breadcrumbs.test.ts +0 -43
  44. package/src/utils/breadcrumbs.ts +1 -37
  45. package/src/utils/navigation.ts +1 -20
  46. package/src/utils/route-config.test.ts +0 -31
  47. package/temp/build/lint/_eslint-5eVG3S6w.json +30 -54
  48. package/src/components/lists/FindingsNav.module.scss +0 -126
  49. package/src/components/lists/FindingsNav.tsx +0 -146
  50. package/src/components/panels/FindingsPanel.module.scss +0 -94
  51. package/src/components/panels/FindingsPanel.stories.tsx +0 -109
  52. package/src/components/panels/FindingsPanel.tsx +0 -76
  53. package/src/components/tools/FindingCard.stories.tsx +0 -124
  54. package/src/components/tools/FindingCard.tsx +0 -178
  55. package/src/utils/findingCategory.ts +0 -33
package/src/index.ts CHANGED
@@ -55,7 +55,7 @@ export { StatusBar, AppNav, Sidebar, BottomStatusBar, TABS } from "./components/
55
55
  export type { AppTab } from "./components/layout/index.js";
56
56
 
57
57
  // Lists
58
- export { EnvironmentNav, FindingsNav } from "./components/lists/index.js";
58
+ export { EnvironmentNav } from "./components/lists/index.js";
59
59
  export { TaskList } from "./components/lists/TaskList.js";
60
60
  export { HighlightedText, buildTaskTree, groupTasksByStatus } from "./components/lists/listHelpers.js";
61
61
 
@@ -65,7 +65,7 @@ export type { CalloutVariant } from "./components/notifications/index.js";
65
65
  export { UpdateBanner } from "./components/notifications/UpdateBanner.js";
66
66
 
67
67
  // Panels
68
- export { FindingsPanel, TokensPanel, AppearancePanel, AboutPanel, TaskEditPanel, TaskActionButtons, TaskOverviewPanel, PluginsPanel, GitHubAccountsPanel } from "./components/panels/index.js";
68
+ export { TokensPanel, AppearancePanel, AboutPanel, TaskEditPanel, TaskActionButtons, TaskOverviewPanel, PluginsPanel, GitHubAccountsPanel } from "./components/panels/index.js";
69
69
  export type { PluginsPanelProps, GitHubAccountsPanelProps } from "./components/panels/index.js";
70
70
  export type { TaskActionButtonsProps } from "./components/panels/TaskActionButtons.js";
71
71
  export type { TaskOverviewPanelProps } from "./components/panels/TaskOverviewPanel.js";
@@ -126,13 +126,13 @@ export type { UseGrackleSocketResult, GrackleContextType } from "./context/Grack
126
126
 
127
127
  export type {
128
128
  Environment, Session, UsageStats, SessionEvent,
129
- Workspace, TaskData, FindingData, TokenInfo,
129
+ Workspace, TaskData, TokenInfo,
130
130
  CredentialProviderConfig, Codespace, DockerContainer, PersonaData,
131
131
  ScheduleData, ScheduleUpdate, UseSchedulesResult,
132
132
  ProvisionStatus, GrackleEvent, WsMessage, SendFunction,
133
133
  GraphNode, GraphLink, NodeDetail, UseKnowledgeResult,
134
134
  UseEnvironmentsResult, UseSessionsResult, UseWorkspacesResult,
135
- UseTasksResult, UseFindingsResult, UseTokensResult,
135
+ UseTasksResult, UseTokensResult,
136
136
  UseCredentialsResult, UseCodespacesResult, UseDockerContainersResult, UsePersonasResult,
137
137
  UsePluginsResult, PluginData,
138
138
  StreamData, StreamSubscriberData, UseStreamsResult,
@@ -164,7 +164,6 @@ export {
164
164
  SCHEDULES_URL, NEW_SCHEDULE_URL, scheduleUrl,
165
165
  SETTINGS_APPEARANCE_URL, SETTINGS_ABOUT_URL, SETTINGS_SHORTCUTS_URL,
166
166
  PAIR_PATH, NEW_WORKSPACE_URL, KNOWLEDGE_URL, HOME_URL,
167
- FINDINGS_URL, findingsUrl, findingUrl,
168
167
  CHAT_URL, COORDINATION_URL, TASKS_URL,
169
168
  } from "./utils/navigation.js";
170
169
 
@@ -177,8 +176,6 @@ export type { TaskStatusKey, VirtualStatus, DisplayStatus, TaskStatusStyle } fro
177
176
 
178
177
  export { formatTokens, formatCost } from "./utils/format.js";
179
178
  export { formatRelativeTime, formatCountdown } from "./utils/time.js";
180
- export { CATEGORY_COLORS, getCategoryColor } from "./utils/findingCategory.js";
181
- export type { CategoryColor } from "./utils/findingCategory.js";
182
179
 
183
180
  export type { BreadcrumbSegment } from "./utils/breadcrumbs.js";
184
181
  export {
@@ -186,7 +183,6 @@ export {
186
183
  buildEnvironmentsBreadcrumbs, buildNewEnvironmentBreadcrumbs,
187
184
  buildNewChatBreadcrumbs, buildSessionBreadcrumbs,
188
185
  buildWorkspaceBreadcrumbs, buildTaskBreadcrumbs, buildNewTaskBreadcrumbs,
189
- buildFindingsBreadcrumbs, buildFindingBreadcrumbs,
190
186
  } from "./utils/breadcrumbs.js";
191
187
 
192
188
  export { groupConsecutiveTextEvents, pairToolEvents } from "./utils/sessionEvents.js";
@@ -24,7 +24,6 @@ import type {
24
24
  Environment,
25
25
  Session,
26
26
  SessionEvent,
27
- FindingData,
28
27
  TaskData,
29
28
  Workspace,
30
29
  TokenInfo,
@@ -48,7 +47,6 @@ import {
48
47
  MOCK_EVENTS,
49
48
  MOCK_WORKSPACES,
50
49
  MOCK_TASKS,
51
- MOCK_FINDINGS,
52
50
  MOCK_TOKENS,
53
51
  MOCK_PERSONAS,
54
52
  MOCK_TASK_SESSIONS,
@@ -97,9 +95,6 @@ export function MockGrackleProvider({ children }: MockGrackleProviderProps): JSX
97
95
  const [workspaces, setWorkspaces] = useState<Workspace[]>(MOCK_WORKSPACES);
98
96
  const [workspaceLinkError, setWorkspaceLinkError] = useState("");
99
97
  const [tasks, setTasks] = useState<TaskData[]>(MOCK_TASKS);
100
- const [findings, setFindings] = useState<FindingData[]>(MOCK_FINDINGS);
101
- const [selectedFinding, setSelectedFinding] = useState<FindingData | undefined>(undefined);
102
- const findingLoading = false;
103
98
  const [tokens, setTokens] = useState<TokenInfo[]>(MOCK_TOKENS);
104
99
  const [credentialProviders, setCredentialProviders] = useState<CredentialProviderConfig>({
105
100
  claude: "off",
@@ -768,59 +763,6 @@ export function MockGrackleProvider({ children }: MockGrackleProviderProps): JSX
768
763
  [],
769
764
  );
770
765
 
771
- /** Filters findings by workspaceId. */
772
- const loadFindings: UseGrackleSocketResult["findings"]["loadFindings"] = useCallback(
773
- async (workspaceId: string) => {
774
- console.log("[MockGrackle] loadFindings", workspaceId);
775
- setFindings(MOCK_FINDINGS.filter((f) => f.workspaceId === workspaceId));
776
- },
777
- [],
778
- );
779
-
780
- /** Load all findings across all workspaces. */
781
- const loadAllFindings: UseGrackleSocketResult["findings"]["loadAllFindings"] = useCallback(async () => {
782
- console.log("[MockGrackle] loadAllFindings");
783
- setFindings([...MOCK_FINDINGS]);
784
- }, []);
785
-
786
- /** Load a single finding by ID. */
787
- const loadFinding: UseGrackleSocketResult["findings"]["loadFinding"] = useCallback(
788
- async (findingId: string) => {
789
- console.log("[MockGrackle] loadFinding", findingId);
790
- const found = MOCK_FINDINGS.find((f) => f.id === findingId);
791
- setSelectedFinding(found);
792
- },
793
- [],
794
- );
795
-
796
- /** Adds a new finding to state. */
797
- const postFinding: UseGrackleSocketResult["findings"]["postFinding"] = useCallback(
798
- async (
799
- workspaceId: string,
800
- title: string,
801
- content: string,
802
- category?: string,
803
- tags?: string[],
804
- ) => {
805
- console.log("[MockGrackle] postFinding", { workspaceId, title });
806
-
807
- const newFinding: FindingData = {
808
- id: nextId("find"),
809
- workspaceId,
810
- taskId: "",
811
- sessionId: "",
812
- category: category || "general",
813
- title,
814
- content,
815
- tags: tags || [],
816
- createdAt: new Date().toISOString(),
817
- };
818
-
819
- setFindings((prev) => [...prev, newFinding]);
820
- },
821
- [nextId],
822
- );
823
-
824
766
  /** No-op in mock mode (environments are pre-seeded). */
825
767
  const loadEnvironments: UseGrackleSocketResult["environments"]["loadEnvironments"] = useCallback(async () => {
826
768
  console.log("[MockGrackle] loadEnvironments");
@@ -1040,17 +982,6 @@ export function MockGrackleProvider({ children }: MockGrackleProviderProps): JSX
1040
982
  domainHook: NOOP_DOMAIN_HOOK,
1041
983
  },
1042
984
 
1043
- findings: {
1044
- findings,
1045
- selectedFinding,
1046
- findingLoading,
1047
- findingsLoading: false,
1048
- loadFindings,
1049
- loadAllFindings,
1050
- loadFinding,
1051
- postFinding,
1052
- domainHook: NOOP_DOMAIN_HOOK,
1053
- },
1054
985
 
1055
986
  tokens: {
1056
987
  tokens,
@@ -1389,8 +1320,6 @@ export function MockGrackleProvider({ children }: MockGrackleProviderProps): JSX
1389
1320
  workspaces,
1390
1321
  workspaceLinkError,
1391
1322
  tasks,
1392
- findings,
1393
- selectedFinding,
1394
1323
  tokens,
1395
1324
  credentialProviders,
1396
1325
  personas,
@@ -1416,10 +1345,6 @@ export function MockGrackleProvider({ children }: MockGrackleProviderProps): JSX
1416
1345
  resumeTask,
1417
1346
  updateTask,
1418
1347
  deleteTask,
1419
- loadFindings,
1420
- loadAllFindings,
1421
- loadFinding,
1422
- postFinding,
1423
1348
  loadEnvironments,
1424
1349
  addEnvironment,
1425
1350
  updateEnvironment,
@@ -2,8 +2,8 @@
2
2
  * Static mock data for visual testing (`?mock` mode).
3
3
  *
4
4
  * Provides realistic sample entities that exercise every UI state:
5
- * multiple environments, sessions in various statuses, workspaces with
6
- * tasks at different lifecycle stages, and findings across all categories.
5
+ * multiple environments, sessions in various statuses, and workspaces with
6
+ * tasks at different lifecycle stages.
7
7
  */
8
8
 
9
9
  import type {
@@ -12,7 +12,6 @@ import type {
12
12
  SessionEvent,
13
13
  Workspace,
14
14
  TaskData,
15
- FindingData,
16
15
  TokenInfo,
17
16
  PersonaData,
18
17
  } from "../hooks/types.js";
@@ -1322,96 +1321,6 @@ export const MOCK_TASKS: TaskData[] = [
1322
1321
  },
1323
1322
  ];
1324
1323
 
1325
- // ─── Findings ───────────────────────────────────────
1326
-
1327
- /** Sample findings across every category to exercise the FindingsPanel styling. */
1328
- export const MOCK_FINDINGS: FindingData[] = [
1329
- {
1330
- id: "find-001",
1331
- workspaceId: "proj-alpha",
1332
- taskId: "task-001",
1333
- sessionId: "sess-001",
1334
- category: "architecture",
1335
- title: "Auth middleware is tightly coupled to Express",
1336
- content:
1337
- "The current auth middleware directly references Express Request/Response types. Consider extracting a framework-agnostic token verification layer so we can reuse it in the WebSocket auth path.",
1338
- tags: ["auth", "decoupling", "middleware"],
1339
- createdAt: "2026-02-27T08:16:00Z",
1340
- },
1341
- {
1342
- id: "find-002",
1343
- workspaceId: "proj-alpha",
1344
- taskId: "task-003",
1345
- sessionId: "sess-002",
1346
- category: "api",
1347
- title: "Missing pagination on GET /api/users",
1348
- content:
1349
- "The users endpoint returns all rows without limit/offset. For datasets over 10k rows this will cause timeouts. Recommend cursor-based pagination with a default page size of 50.",
1350
- tags: ["api", "pagination", "performance"],
1351
- createdAt: "2026-02-27T07:31:00Z",
1352
- },
1353
- {
1354
- id: "find-003",
1355
- workspaceId: "proj-alpha",
1356
- taskId: "task-005",
1357
- sessionId: "sess-003",
1358
- category: "bug",
1359
- title: "Race condition in session cleanup",
1360
- content:
1361
- "When two requests hit /api/logout concurrently, the second call throws a 500 because the session row has already been deleted. Needs an idempotent DELETE or a conditional check.",
1362
- tags: ["bug", "concurrency", "sessions"],
1363
- createdAt: "2026-02-26T22:50:00Z",
1364
- },
1365
- {
1366
- id: "find-004",
1367
- workspaceId: "proj-alpha",
1368
- taskId: "task-004",
1369
- sessionId: "",
1370
- category: "decision",
1371
- title: "Chose pg-pool over knex connection pool",
1372
- content:
1373
- "pg-pool gives us direct control over idle timeout, max connections, and health check queries. Knex wraps pg-pool anyway and adds overhead we don't need since we write raw SQL.",
1374
- tags: ["database", "decision", "postgres"],
1375
- createdAt: "2026-02-23T11:30:00Z",
1376
- },
1377
- {
1378
- id: "find-005",
1379
- workspaceId: "proj-alpha",
1380
- taskId: "",
1381
- sessionId: "",
1382
- category: "dependency",
1383
- title: "jsonwebtoken has 3 high-severity CVEs",
1384
- content:
1385
- "The jsonwebtoken package (v8.x) has known vulnerabilities. Consider migrating to jose which is maintained, supports ESM, and covers the same JWS/JWE surface area with zero dependencies.",
1386
- tags: ["security", "dependency", "jwt"],
1387
- createdAt: "2026-02-27T08:20:00Z",
1388
- },
1389
- {
1390
- id: "find-006",
1391
- workspaceId: "proj-alpha",
1392
- taskId: "task-001",
1393
- sessionId: "sess-001",
1394
- category: "pattern",
1395
- title: "Consistent error response shape",
1396
- content:
1397
- 'All error responses should follow the shape `{ error: string, code: string, details?: unknown }`. Currently some routes return `{ message: string }` and others return `{ error: string }`.',
1398
- tags: ["api", "consistency", "error-handling"],
1399
- createdAt: "2026-02-27T08:17:00Z",
1400
- },
1401
- {
1402
- id: "find-007",
1403
- workspaceId: "proj-beta",
1404
- taskId: "task-007",
1405
- sessionId: "sess-004",
1406
- category: "architecture",
1407
- title: "Watermark storage should be pluggable",
1408
- content:
1409
- "The incremental load watermarks are currently stored in a local SQLite file. For production multi-worker scenarios, this needs to be backed by a shared store (Redis or Postgres).",
1410
- tags: ["architecture", "pipeline", "scalability"],
1411
- createdAt: "2026-02-27T09:05:00Z",
1412
- },
1413
- ];
1414
-
1415
1324
  // ─── Tokens ──────────────────────────────────────────
1416
1325
 
1417
1326
  /** Sample tokens for the settings panel. */
@@ -1457,7 +1366,7 @@ export const MOCK_PERSONAS: PersonaData[] = [
1457
1366
  updatedAt: "2026-02-20T10:00:00Z",
1458
1367
  type: "agent",
1459
1368
  script: "",
1460
- allowedMcpTools: ["task_create", "task_update", "task_list", "finding_post", "finding_list", "knowledge_search", "knowledge_create_node", "session_spawn"],
1369
+ allowedMcpTools: ["task_create", "task_update", "task_list", "knowledge_search", "session_spawn"],
1461
1370
  },
1462
1371
  {
1463
1372
  id: "persona-fe",
@@ -1478,8 +1387,8 @@ export const MOCK_PERSONAS: PersonaData[] = [
1478
1387
  {
1479
1388
  id: "persona-reviewer",
1480
1389
  name: "Code Reviewer",
1481
- description: "Reviews diffs for correctness, security, and style. Posts findings for issues discovered.",
1482
- systemPrompt: "You are a meticulous code reviewer. Check for security vulnerabilities, performance issues, and style consistency. Post findings for anything noteworthy.",
1390
+ description: "Reviews diffs for correctness, security, and style. Reports issues discovered.",
1391
+ systemPrompt: "You are a meticulous code reviewer. Check for security vulnerabilities, performance issues, and style consistency. Report anything noteworthy.",
1483
1392
  toolConfig: JSON.stringify({ allowedTools: ["Read", "Grep", "Glob"] }),
1484
1393
  runtime: "claude-code",
1485
1394
  model: "claude-sonnet-4-6",
@@ -1489,7 +1398,7 @@ export const MOCK_PERSONAS: PersonaData[] = [
1489
1398
  updatedAt: "2026-02-20T10:10:00Z",
1490
1399
  type: "agent",
1491
1400
  script: "",
1492
- allowedMcpTools: ["finding_post", "finding_list", "knowledge_search"],
1401
+ allowedMcpTools: ["knowledge_search"],
1493
1402
  },
1494
1403
  {
1495
1404
  id: "persona-tester",
@@ -1510,7 +1419,7 @@ export const MOCK_PERSONAS: PersonaData[] = [
1510
1419
  {
1511
1420
  id: "persona-lint",
1512
1421
  name: "Lint & Format",
1513
- description: "Scripted persona — runs ESLint and Prettier on changed files, auto-fixes violations, and posts a findings summary.",
1422
+ description: "Scripted persona — runs ESLint and Prettier on changed files, auto-fixes violations, and logs a summary.",
1514
1423
  systemPrompt: "",
1515
1424
  toolConfig: "{}",
1516
1425
  runtime: "genaiscript",
@@ -1520,7 +1429,7 @@ export const MOCK_PERSONAS: PersonaData[] = [
1520
1429
  createdAt: "2026-02-21T09:00:00Z",
1521
1430
  updatedAt: "2026-02-21T09:00:00Z",
1522
1431
  type: "script",
1523
- script: 'const files = env.files.filter(f => /\\.(ts|tsx|js)$/.test(f.filename));\nfor (const f of files) {\n const eslintResult = await host.exec("npx", ["eslint", "--fix", f.filename]);\n if (eslintResult.exitCode !== 0) {\n env.findings.push({ category: "lint", title: `Lint issues in ${f.filename}`, content: eslintResult.stderr });\n }\n const prettierResult = await host.exec("npx", ["prettier", "--write", f.filename]);\n if (prettierResult.exitCode !== 0) {\n env.findings.push({ category: "format", title: `Prettier issues in ${f.filename}`, content: prettierResult.stderr });\n }\n}\nenv.findings.push({ category: "summary", title: "Lint & format pass complete", content: `Ran ESLint and Prettier on ${files.length} files` });',
1432
+ script: 'const files = env.files.filter(f => /\\.(ts|tsx|js)$/.test(f.filename));\nfor (const f of files) {\n const eslintResult = await host.exec("npx", ["eslint", "--fix", f.filename]);\n if (eslintResult.exitCode !== 0) {\n console.log(`Lint issues in ${f.filename}: ${eslintResult.stderr}`);\n }\n const prettierResult = await host.exec("npx", ["prettier", "--write", f.filename]);\n if (prettierResult.exitCode !== 0) {\n console.log(`Prettier issues in ${f.filename}: ${prettierResult.stderr}`);\n }\n}\nconsole.log(`Ran ESLint and Prettier on ${files.length} files`);',
1524
1433
  allowedMcpTools: [],
1525
1434
  },
1526
1435
  ];
@@ -11,7 +11,6 @@ import type {
11
11
  SessionEvent,
12
12
  Workspace,
13
13
  TaskData,
14
- FindingData,
15
14
  TokenInfo,
16
15
  PersonaData,
17
16
  ScheduleData,
@@ -25,7 +24,6 @@ export {
25
24
  MOCK_SESSIONS,
26
25
  MOCK_WORKSPACES,
27
26
  MOCK_TASKS,
28
- MOCK_FINDINGS,
29
27
  MOCK_TOKENS,
30
28
  MOCK_PERSONAS,
31
29
  } from "../mocks/mockData.js";
@@ -111,22 +109,6 @@ export function makeTask(overrides: Partial<TaskData> = {}): TaskData {
111
109
  };
112
110
  }
113
111
 
114
- /** Create a FindingData with sensible defaults. */
115
- export function makeFinding(overrides: Partial<FindingData> = {}): FindingData {
116
- return {
117
- id: nextId("finding"),
118
- workspaceId: "ws-1",
119
- taskId: "task-1",
120
- sessionId: "sess-1",
121
- category: "general",
122
- title: "Test Finding",
123
- content: "Finding content",
124
- tags: [],
125
- createdAt: "2026-01-01T00:00:00Z",
126
- ...overrides,
127
- };
128
- }
129
-
130
112
  /** Create a TokenInfo with sensible defaults. */
131
113
  export function makeToken(overrides: Partial<TokenInfo> = {}): TokenInfo {
132
114
  return {
@@ -253,7 +235,6 @@ export const buildEnvironment: typeof makeEnvironment = makeEnvironment;
253
235
  export const buildSession: typeof makeSession = makeSession;
254
236
  export const buildWorkspace: typeof makeWorkspace = makeWorkspace;
255
237
  export const buildTask: typeof makeTask = makeTask;
256
- export const buildFinding: typeof makeFinding = makeFinding;
257
238
  export const buildToken: typeof makeToken = makeToken;
258
239
  export const buildPersona: typeof makePersona = makePersona;
259
240
  export const buildEvent: typeof makeEvent = makeEvent;
@@ -8,8 +8,6 @@ import {
8
8
  buildNewTaskBreadcrumbs,
9
9
  buildNewChatBreadcrumbs,
10
10
  buildSessionBreadcrumbs,
11
- buildFindingsBreadcrumbs,
12
- buildFindingBreadcrumbs,
13
11
  type BreadcrumbSegment,
14
12
  } from "./breadcrumbs.js";
15
13
  import type { Environment, TaskData, Workspace } from "../hooks/types.js";
@@ -228,47 +226,6 @@ describe("breadcrumb builders", () => {
228
226
  });
229
227
  });
230
228
 
231
- describe("findings breadcrumb builders", () => {
232
- it("buildFindingsBreadcrumbs returns Home > Findings (non-clickable)", () => {
233
- const segments: BreadcrumbSegment[] = buildFindingsBreadcrumbs();
234
- expect(segments).toHaveLength(2);
235
- expect(segments[0].label).toBe("Home");
236
- expect(segments[0].url).toBe("/");
237
- expect(segments[1].label).toBe("Findings");
238
- expect(segments[1].url).toBeUndefined();
239
- });
240
-
241
- it("buildFindingBreadcrumbs without scope returns Home > Findings > Title", () => {
242
- const segments: BreadcrumbSegment[] = buildFindingBreadcrumbs("My Finding", undefined, undefined, [], []);
243
- expect(segments).toHaveLength(3);
244
- expect(segments[0].label).toBe("Home");
245
- expect(segments[0].url).toBe("/");
246
- expect(segments[1].label).toBe("Findings");
247
- expect(segments[1].url).toBe("/findings");
248
- expect(segments[2].label).toBe("My Finding");
249
- expect(segments[2].url).toBeUndefined();
250
- });
251
-
252
- it("buildFindingBreadcrumbs with workspace scope returns full chain", () => {
253
- const workspaces: Workspace[] = [makeWorkspace("ws1", "My Workspace", "env1")];
254
- const environments: Environment[] = [makeEnvironment("env1", "Local Dev")];
255
- const segments: BreadcrumbSegment[] = buildFindingBreadcrumbs("My Finding", "ws1", "env1", workspaces, environments);
256
- expect(segments).toHaveLength(6);
257
- expect(segments[0].label).toBe("Home");
258
- expect(segments[0].url).toBe("/");
259
- expect(segments[1].label).toBe("Environments");
260
- expect(segments[1].url).toBe("/environments");
261
- expect(segments[2].label).toBe("Local Dev");
262
- expect(segments[2].url).toBe("/environments/env1");
263
- expect(segments[3].label).toBe("My Workspace");
264
- expect(segments[3].url).toBe("/environments/env1/workspaces/ws1");
265
- expect(segments[4].label).toBe("Findings");
266
- expect(segments[4].url).toBe("/environments/env1/workspaces/ws1/findings");
267
- expect(segments[5].label).toBe("My Finding");
268
- expect(segments[5].url).toBeUndefined();
269
- });
270
- });
271
-
272
229
  describe("task status helpers", () => {
273
230
  it("maps legacy task statuses to canonical styles", () => {
274
231
  expect(getStatusStyle("pending").label).toBe("Not Started");
@@ -1,5 +1,5 @@
1
1
  import type { Environment, TaskData, Workspace } from "../hooks/types.js";
2
- import { ENVIRONMENTS_URL, environmentUrl, FINDINGS_URL, findingsUrl, HOME_URL, SETTINGS_URL, taskUrl, workspaceUrl } from "./navigation.js";
2
+ import { ENVIRONMENTS_URL, environmentUrl, HOME_URL, SETTINGS_URL, taskUrl, workspaceUrl } from "./navigation.js";
3
3
 
4
4
  /** A single segment in the breadcrumb trail. */
5
5
  export interface BreadcrumbSegment {
@@ -184,39 +184,3 @@ export function buildNewTaskBreadcrumbs(
184
184
  return segments;
185
185
  }
186
186
 
187
- /** Findings breadcrumb segment. */
188
- const FINDINGS_SEGMENT: BreadcrumbSegment = { label: "Findings", url: FINDINGS_URL };
189
-
190
- /** Build breadcrumbs for the findings landing page. */
191
- export function buildFindingsBreadcrumbs(): BreadcrumbSegment[] {
192
- return [HOME_SEGMENT, { label: "Findings", url: undefined }];
193
- }
194
-
195
- /** Build breadcrumbs for a finding detail page, optionally scoped to a workspace. */
196
- export function buildFindingBreadcrumbs(
197
- findingTitle: string,
198
- workspaceId: string | undefined,
199
- environmentId: string | undefined,
200
- workspaces: Workspace[],
201
- environments: Environment[],
202
- ): BreadcrumbSegment[] {
203
- const segments: BreadcrumbSegment[] = [HOME_SEGMENT];
204
-
205
- if (workspaceId && environmentId) {
206
- const workspace = workspaces.find((p) => p.id === workspaceId);
207
- const environment = environments.find((e) => e.id === environmentId);
208
- segments.push(ENVIRONMENTS_SEGMENT);
209
- if (environment) {
210
- segments.push({ label: environment.displayName, url: environmentUrl(environmentId) });
211
- }
212
- if (workspace) {
213
- segments.push({ label: workspace.name, url: workspaceUrl(workspaceId, environmentId) });
214
- }
215
- segments.push({ label: "Findings", url: findingsUrl(workspaceId, environmentId) });
216
- } else {
217
- segments.push(FINDINGS_SEGMENT);
218
- }
219
-
220
- segments.push({ label: findingTitle, url: undefined });
221
- return segments;
222
- }
@@ -45,7 +45,7 @@ export function workspaceUrl(workspaceId: string, environmentId?: string): strin
45
45
  }
46
46
 
47
47
  /** Build URL for a task detail page, optionally targeting a specific tab and workspace/environment scope. */
48
- export function taskUrl(taskId: string, tab?: "stream" | "findings", workspaceId?: string, environmentId?: string): string {
48
+ export function taskUrl(taskId: string, tab?: "stream", workspaceId?: string, environmentId?: string): string {
49
49
  const encodedTaskId = encodeURIComponent(taskId);
50
50
  let base: string;
51
51
  if (workspaceId && environmentId) {
@@ -180,25 +180,6 @@ export const NEW_WORKSPACE_URL: string = "/workspaces/new";
180
180
  /** URL for the knowledge graph explorer page. */
181
181
  export const KNOWLEDGE_URL: string = "/knowledge";
182
182
 
183
- /** URL for the findings landing page. */
184
- export const FINDINGS_URL: string = "/findings";
185
-
186
- /** Build URL for the findings list page, optionally scoped to a workspace. */
187
- export function findingsUrl(workspaceId?: string, environmentId?: string): string {
188
- if (workspaceId && environmentId) {
189
- return `/environments/${encodeURIComponent(environmentId)}/workspaces/${encodeURIComponent(workspaceId)}/findings`;
190
- }
191
- return FINDINGS_URL;
192
- }
193
-
194
- /** Build URL for a finding detail page, optionally scoped to a workspace. */
195
- export function findingUrl(findingId: string, workspaceId?: string, environmentId?: string): string {
196
- const encodedFindingId = encodeURIComponent(findingId);
197
- if (workspaceId && environmentId) {
198
- return `/environments/${encodeURIComponent(environmentId)}/workspaces/${encodeURIComponent(workspaceId)}/findings/${encodedFindingId}`;
199
- }
200
- return `/findings/${encodedFindingId}`;
201
- }
202
183
 
203
184
  /** Build URL for the root-task chat page. */
204
185
  export function chatUrl(): string {
@@ -7,15 +7,12 @@ import {
7
7
  newTaskUrl,
8
8
  newChatUrl,
9
9
  personaUrl,
10
- findingUrl,
11
- findingsUrl,
12
10
  HOME_URL,
13
11
  SETTINGS_URL,
14
12
  PERSONAS_URL,
15
13
  NEW_PERSONA_URL,
16
14
  NEW_ENVIRONMENT_URL,
17
15
  NEW_WORKSPACE_URL,
18
- FINDINGS_URL,
19
16
  } from "./navigation.js";
20
17
 
21
18
  describe("URL builder functions", () => {
@@ -38,10 +35,6 @@ describe("URL builder functions", () => {
38
35
  expect(taskUrl("task-1", "stream")).toBe("/tasks/task-1/stream");
39
36
  });
40
37
 
41
- it("taskUrl with findings tab produces correct path", () => {
42
- expect(taskUrl("task-1", "findings")).toBe("/tasks/task-1/findings");
43
- });
44
-
45
38
  it("taskUrl encodes taskId", () => {
46
39
  expect(taskUrl("has space")).toBe("/tasks/has%20space");
47
40
  expect(taskUrl("has space", "stream")).toBe("/tasks/has%20space/stream");
@@ -50,7 +43,6 @@ describe("URL builder functions", () => {
50
43
  it("taskUrl with workspace and environment produces environment-scoped path", () => {
51
44
  expect(taskUrl("task-1", undefined, "ws-1", "env-1")).toBe("/environments/env-1/workspaces/ws-1/tasks/task-1");
52
45
  expect(taskUrl("task-1", "stream", "ws-1", "env-1")).toBe("/environments/env-1/workspaces/ws-1/tasks/task-1/stream");
53
- expect(taskUrl("task-1", "findings", "ws-1", "env-1")).toBe("/environments/env-1/workspaces/ws-1/tasks/task-1/findings");
54
46
  });
55
47
 
56
48
  it("taskUrl with only workspaceId (no environmentId) falls back to legacy workspace path", () => {
@@ -102,27 +94,4 @@ describe("URL builder functions", () => {
102
94
  expect(personaUrl("has space")).toBe("/settings/personas/has%20space");
103
95
  });
104
96
 
105
- it("FINDINGS_URL constant is correct", () => {
106
- expect(FINDINGS_URL).toBe("/findings");
107
- });
108
-
109
- it("findingUrl produces base path without scope", () => {
110
- expect(findingUrl("f1")).toBe("/findings/f1");
111
- });
112
-
113
- it("findingUrl with workspace and environment produces scoped path", () => {
114
- expect(findingUrl("f1", "ws1", "env1")).toBe("/environments/env1/workspaces/ws1/findings/f1");
115
- });
116
-
117
- it("findingUrl encodes special characters", () => {
118
- expect(findingUrl("has space")).toBe("/findings/has%20space");
119
- });
120
-
121
- it("findingsUrl without scope returns base findings path", () => {
122
- expect(findingsUrl()).toBe("/findings");
123
- });
124
-
125
- it("findingsUrl with workspace and environment produces scoped path", () => {
126
- expect(findingsUrl("ws1", "env1")).toBe("/environments/env1/workspaces/ws1/findings");
127
- });
128
97
  });