@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.
- package/.rush/temp/chunked-rush-logs/web-components._phase_build.chunks.jsonl +6 -6
- package/.rush/temp/chunked-rush-logs/web-components._phase_test.chunks.jsonl +26 -28
- package/.rush/temp/{689afee5625b24607e65ba3a6d3b279d5895784d.tar.log → f238c174d295031bec7f186733732e0fd7e4b9a5.tar.log} +55 -60
- package/.rush/temp/{689afee5625b24607e65ba3a6d3b279d5895784d.untar.log → f238c174d295031bec7f186733732e0fd7e4b9a5.untar.log} +2 -2
- package/.rush/temp/{e12a67384bc67b4cba24253105ba33ed0945c91d.tar.log → f5301e6a84109dcec06242d178df01e555a83456.tar.log} +2 -2
- package/.rush/temp/{e12a67384bc67b4cba24253105ba33ed0945c91d.untar.log → f5301e6a84109dcec06242d178df01e555a83456.untar.log} +2 -2
- package/.rush/temp/operation/_phase_build/all.log +6 -6
- package/.rush/temp/operation/_phase_build/log-chunks.jsonl +6 -6
- package/.rush/temp/operation/_phase_build/state.json +1 -1
- package/.rush/temp/operation/_phase_test/all.log +26 -28
- package/.rush/temp/operation/_phase_test/log-chunks.jsonl +26 -28
- package/.rush/temp/operation/_phase_test/state.json +1 -1
- package/.rush/temp/shrinkwrap-deps.json +3 -3
- package/README.md +2 -2
- package/dist/index.css +1 -1
- package/dist/index.js +9055 -9498
- package/package.json +2 -2
- package/rush-logs/web-components._phase_build.cache.log +1 -1
- package/rush-logs/web-components._phase_build.log +6 -6
- package/rush-logs/web-components._phase_test.cache.log +1 -1
- package/rush-logs/web-components._phase_test.log +26 -28
- package/src/components/index.ts +0 -3
- package/src/components/knowledge/KnowledgeDetailPanel.tsx +1 -4
- package/src/components/layout/AppNav.stories.tsx +3 -6
- package/src/components/layout/AppNav.tsx +3 -7
- package/src/components/layout/BottomStatusBar.tsx +4 -6
- package/src/components/lists/index.ts +0 -1
- package/src/components/panels/KeyboardShortcutsPanel.tsx +0 -1
- package/src/components/panels/index.ts +0 -1
- package/src/components/personas/McpToolSelector.stories.tsx +12 -12
- package/src/components/tools/ToolCard.stories.tsx +0 -26
- package/src/components/tools/ToolCard.tsx +0 -3
- package/src/components/tools/ToolSearchCard.stories.tsx +8 -8
- package/src/components/tools/WorkpadCard.stories.tsx +5 -5
- package/src/components/tools/classifyTool.test.ts +0 -1
- package/src/components/tools/classifyTool.ts +2 -7
- package/src/context/GrackleContextTypes.ts +1 -3
- package/src/hooks/types.ts +1 -44
- package/src/index.ts +4 -8
- package/src/mocks/MockGrackleProvider.tsx +0 -75
- package/src/mocks/mockData.ts +8 -99
- package/src/test-utils/storybook-helpers.ts +0 -19
- package/src/utils/breadcrumbs.test.ts +0 -43
- package/src/utils/breadcrumbs.ts +1 -37
- package/src/utils/navigation.ts +1 -20
- package/src/utils/route-config.test.ts +0 -31
- package/temp/build/lint/_eslint-5eVG3S6w.json +30 -54
- package/src/components/lists/FindingsNav.module.scss +0 -126
- package/src/components/lists/FindingsNav.tsx +0 -146
- package/src/components/panels/FindingsPanel.module.scss +0 -94
- package/src/components/panels/FindingsPanel.stories.tsx +0 -109
- package/src/components/panels/FindingsPanel.tsx +0 -76
- package/src/components/tools/FindingCard.stories.tsx +0 -124
- package/src/components/tools/FindingCard.tsx +0 -178
- 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
|
|
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 {
|
|
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,
|
|
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,
|
|
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,
|
package/src/mocks/mockData.ts
CHANGED
|
@@ -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
|
|
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", "
|
|
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.
|
|
1482
|
-
systemPrompt: "You are a meticulous code reviewer. Check for security vulnerabilities, performance issues, and style consistency.
|
|
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: ["
|
|
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
|
|
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
|
|
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");
|
package/src/utils/breadcrumbs.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Environment, TaskData, Workspace } from "../hooks/types.js";
|
|
2
|
-
import { ENVIRONMENTS_URL, environmentUrl,
|
|
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
|
-
}
|
package/src/utils/navigation.ts
CHANGED
|
@@ -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"
|
|
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
|
});
|