@elevasis/ui 2.22.0 → 2.23.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 (35) hide show
  1. package/dist/{chunk-LKVBEE63.js → chunk-7PGEGSUM.js} +1 -1
  2. package/dist/{chunk-GJ7EIABJ.js → chunk-D3KQAABP.js} +1 -1
  3. package/dist/{chunk-IOTLB6ND.js → chunk-EPV7NU2E.js} +22 -8
  4. package/dist/{chunk-WWJ6S2HQ.js → chunk-FXWETLEB.js} +1 -1
  5. package/dist/{chunk-B4FHWKEF.js → chunk-GUJUK6EH.js} +22 -9
  6. package/dist/{chunk-LVJGPE6H.js → chunk-N6WLOWOD.js} +1 -1
  7. package/dist/{chunk-BSZRKBAW.js → chunk-PTUOINQ2.js} +1 -1
  8. package/dist/{chunk-COG4ABRI.js → chunk-PXGSJNBH.js} +1 -1
  9. package/dist/{chunk-IBUYJXA3.js → chunk-YU6MBDVO.js} +962 -343
  10. package/dist/components/index.d.ts +12 -14
  11. package/dist/components/index.js +29 -22
  12. package/dist/features/auth/index.d.ts +9 -12
  13. package/dist/features/crm/index.d.ts +9 -12
  14. package/dist/features/crm/index.js +2 -2
  15. package/dist/features/dashboard/index.js +2 -2
  16. package/dist/features/delivery/index.d.ts +9 -12
  17. package/dist/features/delivery/index.js +2 -2
  18. package/dist/features/lead-gen/index.js +2 -2
  19. package/dist/features/monitoring/index.js +3 -3
  20. package/dist/features/monitoring/requests/index.js +1 -1
  21. package/dist/features/operations/index.js +4 -4
  22. package/dist/features/settings/index.d.ts +9 -12
  23. package/dist/features/settings/index.js +2 -2
  24. package/dist/hooks/delivery/index.d.ts +9 -12
  25. package/dist/hooks/index.d.ts +33 -28
  26. package/dist/hooks/index.js +1 -1
  27. package/dist/hooks/published.d.ts +33 -28
  28. package/dist/hooks/published.js +1 -1
  29. package/dist/index.d.ts +33 -28
  30. package/dist/index.js +1 -1
  31. package/dist/initialization/index.d.ts +9 -12
  32. package/dist/profile/index.d.ts +9 -12
  33. package/dist/supabase/index.d.ts +18 -24
  34. package/dist/types/index.d.ts +9 -12
  35. package/package.json +4 -4
@@ -1,14 +1,14 @@
1
1
  import { ChatHeader, ChatSidebar } from './chunk-ROSMICXG.js';
2
2
  import { SubshellSidebarLoader, SubshellLoader } from './chunk-3JCMO7SD.js';
3
- import { BaseNode, useGraphTheme, BaseEdge, FormFieldRenderer, ExecutionStats, UnifiedWorkflowGraph, WorkflowExecutionTimeline, AgentExecutionVisualizer, AgentExecutionTimeline, GraphBackground, GraphFitViewButton, GraphFitViewHandler } from './chunk-LKVBEE63.js';
4
- import { ResourceHealthPanel } from './chunk-GJ7EIABJ.js';
3
+ import { BaseNode, useGraphTheme, BaseEdge, FormFieldRenderer, ExecutionStats, UnifiedWorkflowGraph, WorkflowExecutionTimeline, AgentExecutionVisualizer, AgentExecutionTimeline, GraphBackground, GraphFitViewButton, GraphFitViewHandler } from './chunk-7PGEGSUM.js';
4
+ import { ResourceHealthPanel } from './chunk-D3KQAABP.js';
5
5
  import { useCyberColors, CyberDonut } from './chunk-TSSKOQBX.js';
6
6
  import { AppShellLoader } from './chunk-M25JL54Z.js';
7
7
  import { PageContainer } from './chunk-BZZCNLT6.js';
8
8
  import { SubshellSidebarSection } from './chunk-IIMU5YAJ.js';
9
9
  import { CustomModal, ConfirmationModal } from './chunk-KVJ3LFH2.js';
10
10
  import { getResourceStatusColor, useTimelineData, useAgentIterationData, getStatusIcon } from './chunk-E4WQGJNS.js';
11
- import { useErrorDetail, useExecution, useArchivedLogs, useDeleteExecution, useRetryExecution, useCancelExecution, useCommandQueueTotals, useStatusFilter, useResourceSearch, useResourcesDomainFilters, usePaginationState, useResources, useRecentExecutionsByResource, filterByDomainFilters, collectResourceFilterFacets, useExecuteAsync, useResourceDefinition, isSessionCapable, useDeleteTask, useCommandQueue, useSubmitAction, useCommandViewData, useCommandViewStats, useCommandViewStore, useResourceExecutions, useCheckpointTasks, COMMAND_VIEW_VISUALIZATION_MODES, useExecutionPanelState, useDeleteSession, useCreateSession, useSessions, useSessionExecutions, useSession, useBulkDeleteExecutions, getCommandViewGraphPositions } from './chunk-B4FHWKEF.js';
11
+ import { useErrorDetail, useExecution, useArchivedLogs, useDeleteExecution, useRetryExecution, useCancelExecution, useCommandQueueTotals, useStatusFilter, useResourceSearch, useResourcesDomainFilters, usePaginationState, useResources, useRecentExecutionsByResource, filterByDomainFilters, collectResourceFilterFacets, useExecuteAsync, useResourceDefinition, isSessionCapable, useDeleteTask, useCommandQueue, useSubmitAction, useCommandViewData, useCommandViewStats, useCommandViewStore, useResourceExecutions, useCheckpointTasks, COMMAND_VIEW_VISUALIZATION_MODES, useExecutionPanelState, useDeleteSession, useCreateSession, useSessions, useSessionExecutions, useSession, useBulkDeleteExecutions, getCommandViewGraphPositions } from './chunk-GUJUK6EH.js';
12
12
  import { showApiErrorNotification, showSuccessNotification } from './chunk-Z6FAH4XV.js';
13
13
  import { useGraphHighlighting, calculateGraphHeight, Graph_module_css_default, GRAPH_CONSTANTS } from './chunk-22UVE3RA.js';
14
14
  import { useMergedExecution } from './chunk-3ZMAGTWF.js';
@@ -25,7 +25,7 @@ import { ResourceStatusColors, toWorkflowLogMessages } from './chunk-KRWALB24.js
25
25
  import { useInitialization } from './chunk-DK2HVHCY.js';
26
26
  import { useOrganization } from './chunk-DD3CCMCZ.js';
27
27
  import { z } from 'zod';
28
- import { Stack, Group, Text, Badge, Title, Textarea, Alert, Button, ActionIcon, Collapse, Card, ThemeIcon, SimpleGrid, Divider, Paper, Space, CopyButton, Center, Tooltip, Code, Menu, useMantineTheme, UnstyledButton, Select, RangeSlider, Box, Progress, Tabs, Pagination, TextInput, Modal, LoadingOverlay, Loader, SegmentedControl, ScrollArea, Checkbox, MultiSelect, Switch } from '@mantine/core';
28
+ import { Stack, Group, Text, Badge, Title, Textarea, Alert, Button, ActionIcon, Collapse, Card, ThemeIcon, SimpleGrid, Divider, Paper, Space, CopyButton, Center, Tooltip, Code, Menu, useMantineTheme, UnstyledButton, Select, RangeSlider, Box, Progress, Tabs, Pagination, TextInput, Modal, LoadingOverlay, Loader, SegmentedControl, ScrollArea, Checkbox, NumberInput, MultiSelect, Switch } from '@mantine/core';
29
29
  import { IconBrain, IconFileText, IconMail, IconSend, IconClock, IconArrowUp, IconMessageCircle, IconRocket, IconEye, IconEdit, IconAlertTriangle, IconRefresh, IconX, IconCheck, IconCode, IconAlertCircle, IconChevronRight, IconTool, IconSettings, IconCpu, IconClockHour4, IconVersions, IconPlayerPlay, IconNetwork, IconSitemap, IconCopy, IconPlayerStop, IconReload, IconTrash, IconTerminal2, IconBug, IconChevronDown, IconMessage, IconArrowLeft, IconRobot, IconGitBranch, IconDotsVertical, IconFilter, IconCircleCheck, IconCategory, IconDatabase, IconApps, IconRoute, IconAdjustmentsHorizontal, IconSearch, IconCircleX, IconCircleDashed, IconExternalLink, IconFolders, IconBraces, IconBolt, IconTopologyStar3, IconInfoCircle, IconPlus, IconLayoutSidebarRightExpand, IconNote, IconArchive, IconDownload, IconTimeline, IconActivityHeartbeat, IconClockPause, IconArrowsMaximize, IconShare2, IconHistory } from '@tabler/icons-react';
30
30
  import { useForm } from '@mantine/form';
31
31
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
@@ -4684,7 +4684,7 @@ function titleCase2(value) {
4684
4684
  return value.charAt(0).toUpperCase() + value.slice(1);
4685
4685
  }
4686
4686
  function MetricCard({ metric }) {
4687
- return /* @__PURE__ */ jsx(Paper, { withBorder: true, radius: "md", p: "sm", style: { background: "var(--mantine-color-body)" }, children: /* @__PURE__ */ jsxs(Stack, { gap: 2, children: [
4687
+ return /* @__PURE__ */ jsx(Card, { withBorder: true, radius: "md", p: "sm", children: /* @__PURE__ */ jsxs(Stack, { gap: 2, children: [
4688
4688
  /* @__PURE__ */ jsx(Text, { size: "xs", tt: "uppercase", fw: 700, c: "dimmed", children: metric.label }),
4689
4689
  /* @__PURE__ */ jsx(Text, { size: "lg", fw: 800, children: metric.value }),
4690
4690
  metric.hint ? /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: metric.hint }) : null
@@ -4769,6 +4769,7 @@ function OrganizationGraphDetailPanel({
4769
4769
  selectedElement,
4770
4770
  supplementalSummary,
4771
4771
  followUpSections,
4772
+ expandAroundPanel,
4772
4773
  onClearSelection,
4773
4774
  className
4774
4775
  }) {
@@ -4806,6 +4807,7 @@ function OrganizationGraphDetailPanel({
4806
4807
  onClearSelection ? /* @__PURE__ */ jsx(Button, { size: "xs", variant: "subtle", onClick: onClearSelection, children: "Clear" }) : null
4807
4808
  ] }),
4808
4809
  /* @__PURE__ */ jsx(SimpleGrid, { cols: { base: 2, md: 4 }, spacing: "sm", children: state.metrics.map((metric) => /* @__PURE__ */ jsx(MetricCard, { metric }, metric.label)) }),
4810
+ expandAroundPanel,
4809
4811
  supplementalSummary && (supplementalSummary.metrics.length > 0 || supplementalSummary.metadata.length > 0) ? /* @__PURE__ */ jsxs(Fragment, { children: [
4810
4812
  /* @__PURE__ */ jsx(
4811
4813
  Divider,
@@ -5833,6 +5835,717 @@ function buildCommandViewDrillDownSections({
5833
5835
  }
5834
5836
  return sections;
5835
5837
  }
5838
+
5839
+ // src/features/operations/organization-graph/path-tracing/trace.ts
5840
+ var NODE_KIND_ORDER = {
5841
+ organization: 0,
5842
+ feature: 1,
5843
+ surface: 2,
5844
+ capability: 3,
5845
+ entity: 4,
5846
+ resource: 5
5847
+ };
5848
+ var NODE_KIND_LABEL = {
5849
+ organization: "Organization",
5850
+ feature: "Feature",
5851
+ surface: "Surface",
5852
+ capability: "Capability",
5853
+ entity: "Entity",
5854
+ resource: "Resource"
5855
+ };
5856
+ function getNodeLabel(node) {
5857
+ return node.label || node.sourceId || node.id;
5858
+ }
5859
+ function compareTraceNodes(a, b) {
5860
+ const kindDelta = NODE_KIND_ORDER[a.kind] - NODE_KIND_ORDER[b.kind];
5861
+ if (kindDelta !== 0) {
5862
+ return kindDelta;
5863
+ }
5864
+ const labelDelta = getNodeLabel(a).localeCompare(getNodeLabel(b));
5865
+ if (labelDelta !== 0) {
5866
+ return labelDelta;
5867
+ }
5868
+ return a.id.localeCompare(b.id);
5869
+ }
5870
+ function buildMissingNodeMessage(selection, missingNodeIds) {
5871
+ if (missingNodeIds.length === 0) {
5872
+ return "The selected trace is not available.";
5873
+ }
5874
+ if (missingNodeIds.length === 1) {
5875
+ const missingId = missingNodeIds[0];
5876
+ return `Graph node "${missingId}" is not available.`;
5877
+ }
5878
+ return `Graph nodes "${selection.sourceId}" and "${selection.targetId}" are not available.`;
5879
+ }
5880
+ function buildPathNotFoundMessage(source, target) {
5881
+ return `No directed path found from "${source.label}" to "${target.label}".`;
5882
+ }
5883
+ function buildOrganizationGraphTraceIndex(graph) {
5884
+ const nodesById = /* @__PURE__ */ new Map();
5885
+ const edgesById = /* @__PURE__ */ new Map();
5886
+ const outgoingEdgesByNodeId = /* @__PURE__ */ new Map();
5887
+ const incomingEdgesByNodeId = /* @__PURE__ */ new Map();
5888
+ for (const node of graph.nodes) {
5889
+ nodesById.set(node.id, node);
5890
+ outgoingEdgesByNodeId.set(node.id, []);
5891
+ incomingEdgesByNodeId.set(node.id, []);
5892
+ }
5893
+ for (const edge of graph.edges) {
5894
+ edgesById.set(edge.id, edge);
5895
+ const outgoingEdges = outgoingEdgesByNodeId.get(edge.sourceId);
5896
+ if (outgoingEdges) {
5897
+ outgoingEdges.push(edge);
5898
+ }
5899
+ const incomingEdges = incomingEdgesByNodeId.get(edge.targetId);
5900
+ if (incomingEdges) {
5901
+ incomingEdges.push(edge);
5902
+ }
5903
+ }
5904
+ return {
5905
+ nodesById,
5906
+ edgesById,
5907
+ outgoingEdgesByNodeId,
5908
+ incomingEdgesByNodeId
5909
+ };
5910
+ }
5911
+ function getOrganizationGraphTraceNodeKindLabel(kind) {
5912
+ return NODE_KIND_LABEL[kind];
5913
+ }
5914
+ function formatOrganizationGraphTraceNodeOptionLabel(option) {
5915
+ return `${option.label} \xB7 ${getOrganizationGraphTraceNodeKindLabel(option.kind)}`;
5916
+ }
5917
+ function getOrganizationGraphTraceNodeOptions(graph) {
5918
+ return [...graph.nodes].sort(compareTraceNodes).map((node) => ({
5919
+ id: node.id,
5920
+ label: getNodeLabel(node),
5921
+ kind: node.kind,
5922
+ sourceId: node.sourceId,
5923
+ description: node.description,
5924
+ enabled: node.enabled
5925
+ }));
5926
+ }
5927
+ function resolveOrganizationGraphPathTrace(graph, selection) {
5928
+ const index = buildOrganizationGraphTraceIndex(graph);
5929
+ const source = selection.sourceId ? index.nodesById.get(selection.sourceId) ?? null : null;
5930
+ const target = selection.targetId ? index.nodesById.get(selection.targetId) ?? null : null;
5931
+ const missingNodeIds = [
5932
+ selection.sourceId && !source ? selection.sourceId : null,
5933
+ selection.targetId && !target ? selection.targetId : null
5934
+ ].filter((value) => Boolean(value));
5935
+ if (!selection.sourceId && !selection.targetId) {
5936
+ return {
5937
+ status: "idle",
5938
+ selection,
5939
+ source,
5940
+ target,
5941
+ missingNodeIds: [],
5942
+ pathNodes: [],
5943
+ pathEdges: [],
5944
+ highlightNodeIds: [],
5945
+ highlightEdgeIds: [],
5946
+ distance: 0,
5947
+ message: "Select a source and target to trace a path."
5948
+ };
5949
+ }
5950
+ if (!selection.sourceId || !selection.targetId) {
5951
+ return {
5952
+ status: "incomplete",
5953
+ selection,
5954
+ source,
5955
+ target,
5956
+ missingNodeIds,
5957
+ pathNodes: [],
5958
+ pathEdges: [],
5959
+ highlightNodeIds: [],
5960
+ highlightEdgeIds: [],
5961
+ distance: 0,
5962
+ message: "Select both a source and a target to resolve a path."
5963
+ };
5964
+ }
5965
+ if (missingNodeIds.length > 0 || !source || !target) {
5966
+ return {
5967
+ status: "missing-node",
5968
+ selection,
5969
+ source,
5970
+ target,
5971
+ missingNodeIds,
5972
+ pathNodes: [],
5973
+ pathEdges: [],
5974
+ highlightNodeIds: [],
5975
+ highlightEdgeIds: [],
5976
+ distance: 0,
5977
+ message: buildMissingNodeMessage(selection, missingNodeIds)
5978
+ };
5979
+ }
5980
+ if (source.id === target.id) {
5981
+ return {
5982
+ status: "found",
5983
+ selection,
5984
+ source,
5985
+ target,
5986
+ missingNodeIds: [],
5987
+ pathNodes: [source],
5988
+ pathEdges: [],
5989
+ highlightNodeIds: [source.id],
5990
+ highlightEdgeIds: [],
5991
+ distance: 0,
5992
+ message: `Source and target already point to "${source.label}".`
5993
+ };
5994
+ }
5995
+ const visitedNodeIds = /* @__PURE__ */ new Set([source.id]);
5996
+ const predecessorByNodeId = /* @__PURE__ */ new Map();
5997
+ const queue = [source.id];
5998
+ while (queue.length > 0) {
5999
+ const currentNodeId = queue.shift();
6000
+ if (!currentNodeId) {
6001
+ continue;
6002
+ }
6003
+ const outgoingEdges = index.outgoingEdgesByNodeId.get(currentNodeId) ?? [];
6004
+ for (const edge of outgoingEdges) {
6005
+ if (visitedNodeIds.has(edge.targetId)) {
6006
+ continue;
6007
+ }
6008
+ visitedNodeIds.add(edge.targetId);
6009
+ predecessorByNodeId.set(edge.targetId, {
6010
+ nodeId: currentNodeId,
6011
+ edgeId: edge.id
6012
+ });
6013
+ if (edge.targetId === target.id) {
6014
+ queue.length = 0;
6015
+ break;
6016
+ }
6017
+ queue.push(edge.targetId);
6018
+ }
6019
+ }
6020
+ if (!predecessorByNodeId.has(target.id)) {
6021
+ return {
6022
+ status: "not-found",
6023
+ selection,
6024
+ source,
6025
+ target,
6026
+ missingNodeIds: [],
6027
+ pathNodes: [],
6028
+ pathEdges: [],
6029
+ highlightNodeIds: [],
6030
+ highlightEdgeIds: [],
6031
+ distance: 0,
6032
+ message: buildPathNotFoundMessage(source, target)
6033
+ };
6034
+ }
6035
+ const pathNodes = [target];
6036
+ const pathEdges = [];
6037
+ let cursorNodeId = target.id;
6038
+ while (cursorNodeId !== source.id) {
6039
+ const predecessor = predecessorByNodeId.get(cursorNodeId);
6040
+ if (!predecessor) {
6041
+ break;
6042
+ }
6043
+ const edge = index.edgesById.get(predecessor.edgeId);
6044
+ const previousNode = index.nodesById.get(predecessor.nodeId);
6045
+ if (edge) {
6046
+ pathEdges.push(edge);
6047
+ }
6048
+ if (previousNode) {
6049
+ pathNodes.push(previousNode);
6050
+ }
6051
+ cursorNodeId = predecessor.nodeId;
6052
+ }
6053
+ pathNodes.reverse();
6054
+ pathEdges.reverse();
6055
+ return {
6056
+ status: "found",
6057
+ selection,
6058
+ source,
6059
+ target,
6060
+ missingNodeIds: [],
6061
+ pathNodes,
6062
+ pathEdges,
6063
+ highlightNodeIds: pathNodes.map((node) => node.id),
6064
+ highlightEdgeIds: pathEdges.map((edge) => edge.id),
6065
+ distance: pathEdges.length,
6066
+ message: `Path found from "${source.label}" to "${target.label}" across ${pathEdges.length} edge${pathEdges.length === 1 ? "" : "s"}.`
6067
+ };
6068
+ }
6069
+
6070
+ // src/features/operations/organization-graph/expand-around/expandAroundGraph.ts
6071
+ var DEFAULT_MAX_DEPTH = 1;
6072
+ var DEFAULT_MAX_RESULTS = 25;
6073
+ var ORG_MODEL_ROOT_KINDS = /* @__PURE__ */ new Set([
6074
+ "feature",
6075
+ "surface",
6076
+ "entity",
6077
+ "capability"
6078
+ ]);
6079
+ var PRESET_EDGE_KINDS = {
6080
+ coverage: ["contains", "exposes", "operates-on", "maps_to", "references"],
6081
+ "operational-dependencies": ["references"],
6082
+ "org-context": ["contains", "exposes", "operates-on", "maps_to"],
6083
+ "impact-path": ["references"]
6084
+ };
6085
+ var PRESET_RELATIONSHIP_TYPES = {
6086
+ "operational-dependencies": ["triggers", "uses", "approval"],
6087
+ "impact-path": ["triggers", "uses", "approval"]
6088
+ };
6089
+ var PRESET_DIRECTIONS = {
6090
+ coverage: "both",
6091
+ "operational-dependencies": "both",
6092
+ "org-context": "both",
6093
+ "impact-path": "both"
6094
+ };
6095
+ function toSet(values) {
6096
+ if (!values || values.length === 0) {
6097
+ return null;
6098
+ }
6099
+ return new Set(values);
6100
+ }
6101
+ function clampMaxDepth(maxDepth) {
6102
+ if (maxDepth === 2 || maxDepth === 3) {
6103
+ return maxDepth;
6104
+ }
6105
+ return 1;
6106
+ }
6107
+ function resolvePreset(rootNode, preset) {
6108
+ if (preset) {
6109
+ return preset;
6110
+ }
6111
+ if (!rootNode) {
6112
+ return null;
6113
+ }
6114
+ if (ORG_MODEL_ROOT_KINDS.has(rootNode.kind)) {
6115
+ return "coverage";
6116
+ }
6117
+ if (rootNode.kind === "resource") {
6118
+ return "operational-dependencies";
6119
+ }
6120
+ return null;
6121
+ }
6122
+ function resolveRequest(rootNode, request) {
6123
+ const preset = resolvePreset(rootNode, request.preset);
6124
+ const maxResults = request.maxResults === void 0 || !Number.isFinite(request.maxResults) ? DEFAULT_MAX_RESULTS : Math.max(0, request.maxResults);
6125
+ return {
6126
+ rootNodeId: request.rootNodeId,
6127
+ direction: request.direction ?? (preset ? PRESET_DIRECTIONS[preset] : "both"),
6128
+ maxDepth: clampMaxDepth(request.maxDepth ?? DEFAULT_MAX_DEPTH),
6129
+ maxResults,
6130
+ edgeKinds: toSet(request.edgeKinds ?? (preset ? PRESET_EDGE_KINDS[preset] : void 0)),
6131
+ relationshipTypes: toSet(
6132
+ request.relationshipTypes ?? (preset ? PRESET_RELATIONSHIP_TYPES[preset] : void 0)
6133
+ ),
6134
+ preset,
6135
+ nodeKinds: toSet(request.nodeKinds),
6136
+ resourceTypes: toSet(request.resourceTypes),
6137
+ includeHiddenResources: request.includeHiddenResources ?? false
6138
+ };
6139
+ }
6140
+ function getOppositeNodeId(edge, currentNodeId) {
6141
+ if (edge.sourceId === currentNodeId) {
6142
+ return edge.targetId;
6143
+ }
6144
+ if (edge.targetId === currentNodeId) {
6145
+ return edge.sourceId;
6146
+ }
6147
+ return null;
6148
+ }
6149
+ function getCandidateEdges(index, nodeId2, direction) {
6150
+ if (direction === "outgoing") {
6151
+ return index.outgoingEdgesByNodeId.get(nodeId2) ?? [];
6152
+ }
6153
+ if (direction === "incoming") {
6154
+ return index.incomingEdgesByNodeId.get(nodeId2) ?? [];
6155
+ }
6156
+ const seenEdgeIds = /* @__PURE__ */ new Set();
6157
+ const edges = [];
6158
+ for (const edge of [
6159
+ ...index.outgoingEdgesByNodeId.get(nodeId2) ?? [],
6160
+ ...index.incomingEdgesByNodeId.get(nodeId2) ?? []
6161
+ ]) {
6162
+ if (seenEdgeIds.has(edge.id)) {
6163
+ continue;
6164
+ }
6165
+ seenEdgeIds.add(edge.id);
6166
+ edges.push(edge);
6167
+ }
6168
+ return edges;
6169
+ }
6170
+ function matchesPresetTraversalDirection(edge, currentNodeId, request) {
6171
+ if (request.preset === "coverage" && (edge.kind === "contains" || edge.kind === "exposes")) {
6172
+ return edge.sourceId === currentNodeId;
6173
+ }
6174
+ return true;
6175
+ }
6176
+ function matchesEdgeFilters(edge, currentNodeId, request) {
6177
+ if (request.edgeKinds && !request.edgeKinds.has(edge.kind)) {
6178
+ return false;
6179
+ }
6180
+ if (request.relationshipTypes && (!edge.relationshipType || !request.relationshipTypes.has(edge.relationshipType))) {
6181
+ return false;
6182
+ }
6183
+ return matchesPresetTraversalDirection(edge, currentNodeId, request);
6184
+ }
6185
+ function matchesNodeFilters(node, request) {
6186
+ if (request.nodeKinds && !request.nodeKinds.has(node.kind)) {
6187
+ return false;
6188
+ }
6189
+ if (request.resourceTypes) {
6190
+ if (node.kind !== "resource") {
6191
+ return false;
6192
+ }
6193
+ if (!node.resourceType || !request.resourceTypes.has(node.resourceType)) {
6194
+ return false;
6195
+ }
6196
+ }
6197
+ return true;
6198
+ }
6199
+ function pluralize(count, singular, plural = `${singular}s`) {
6200
+ return `${count} ${count === 1 ? singular : plural}`;
6201
+ }
6202
+ function getNodeDisplayName(node) {
6203
+ return node.label || node.sourceId || node.id;
6204
+ }
6205
+ function buildSummaryMessages(input) {
6206
+ const { rootNode, preset, expandedNodes, counts, truncated } = input;
6207
+ if (!rootNode) {
6208
+ return ["Select an available node to expand around."];
6209
+ }
6210
+ if (!preset && rootNode.kind === "organization") {
6211
+ return ["Choose a feature, surface, capability, entity, or resource before expanding."];
6212
+ }
6213
+ if (expandedNodes.length === 0) {
6214
+ return [`No matching graph context found around "${getNodeDisplayName(rootNode)}".`];
6215
+ }
6216
+ const resourceCount = expandedNodes.filter((node) => node.kind === "resource").length;
6217
+ const semanticContextCount = expandedNodes.length - resourceCount;
6218
+ const rootLabel = getNodeDisplayName(rootNode);
6219
+ const messages = [];
6220
+ if (preset === "coverage" && ORG_MODEL_ROOT_KINDS.has(rootNode.kind)) {
6221
+ if (resourceCount > 0 && semanticContextCount > 0) {
6222
+ messages.push(
6223
+ `${pluralize(resourceCount, "resource")} and ${pluralize(
6224
+ semanticContextCount,
6225
+ "semantic context node"
6226
+ )} support "${rootLabel}".`
6227
+ );
6228
+ } else if (resourceCount > 0) {
6229
+ messages.push(`${pluralize(resourceCount, "resource")} support "${rootLabel}".`);
6230
+ } else {
6231
+ messages.push(
6232
+ `${pluralize(semanticContextCount, "semantic context node")} found around "${rootLabel}"; no mapped resources matched this coverage preset.`
6233
+ );
6234
+ }
6235
+ } else if (preset === "operational-dependencies") {
6236
+ messages.push(`${pluralize(resourceCount, "operational dependency resource")} found around "${rootLabel}".`);
6237
+ } else if (preset === "org-context") {
6238
+ messages.push(`${pluralize(expandedNodes.length, "semantic context node")} found around "${rootLabel}".`);
6239
+ } else if (preset === "impact-path") {
6240
+ messages.push(`${pluralize(resourceCount, "resource")} can call or be affected by "${rootLabel}".`);
6241
+ } else {
6242
+ messages.push(`${pluralize(expandedNodes.length, "node")} found around "${rootLabel}".`);
6243
+ }
6244
+ if (counts.hiddenResourceNodes > 0) {
6245
+ messages.push(`${pluralize(counts.hiddenResourceNodes, "hidden resource")} matched current visibility settings.`);
6246
+ }
6247
+ if (counts.alreadyVisibleNodes > 0) {
6248
+ messages.push(`${pluralize(counts.alreadyVisibleNodes, "node")} already visible in the graph.`);
6249
+ }
6250
+ if (truncated) {
6251
+ messages.push(`Results were limited to ${pluralize(expandedNodes.length, "node")}.`);
6252
+ }
6253
+ return messages;
6254
+ }
6255
+ function expandAroundGraph(graph, request, options = {}) {
6256
+ const index = buildOrganizationGraphTraceIndex(graph);
6257
+ const rootNode = index.nodesById.get(request.rootNodeId) ?? null;
6258
+ const resolved = resolveRequest(rootNode, request);
6259
+ const alreadyVisibleNodeIds = new Set(options.alreadyVisibleNodeIds ?? []);
6260
+ const hiddenResourceNodeIds = new Set(options.hiddenResourceNodeIds ?? []);
6261
+ const expandedNodeIds = /* @__PURE__ */ new Set();
6262
+ const expandedEdgeIds = /* @__PURE__ */ new Set();
6263
+ const frontierNodeIds = /* @__PURE__ */ new Set();
6264
+ const visitedDepthByNodeId = /* @__PURE__ */ new Map([[resolved.rootNodeId, 0]]);
6265
+ const hasExplicitTraversalFilter = Boolean(
6266
+ request.preset || request.edgeKinds?.length || request.relationshipTypes?.length || request.nodeKinds?.length || request.resourceTypes?.length
6267
+ );
6268
+ const queue = rootNode && (resolved.preset || hasExplicitTraversalFilter) ? [{ nodeId: rootNode.id, depth: 0 }] : [];
6269
+ let truncated = false;
6270
+ for (let queueIndex = 0; queueIndex < queue.length; queueIndex += 1) {
6271
+ const current = queue[queueIndex];
6272
+ if (current.depth >= resolved.maxDepth) {
6273
+ if (current.nodeId !== resolved.rootNodeId) {
6274
+ frontierNodeIds.add(current.nodeId);
6275
+ }
6276
+ continue;
6277
+ }
6278
+ for (const edge of getCandidateEdges(index, current.nodeId, resolved.direction)) {
6279
+ if (!matchesEdgeFilters(edge, current.nodeId, resolved)) {
6280
+ continue;
6281
+ }
6282
+ const nextNodeId = getOppositeNodeId(edge, current.nodeId);
6283
+ if (!nextNodeId) {
6284
+ continue;
6285
+ }
6286
+ const nextNode = index.nodesById.get(nextNodeId);
6287
+ if (!nextNode || !matchesNodeFilters(nextNode, resolved)) {
6288
+ continue;
6289
+ }
6290
+ if (!resolved.includeHiddenResources && hiddenResourceNodeIds.has(nextNode.id)) {
6291
+ continue;
6292
+ }
6293
+ const nextDepth = current.depth + 1;
6294
+ const previousDepth = visitedDepthByNodeId.get(nextNode.id);
6295
+ if (previousDepth !== void 0) {
6296
+ if (nextNode.id === resolved.rootNodeId || expandedNodeIds.has(nextNode.id)) {
6297
+ expandedEdgeIds.add(edge.id);
6298
+ }
6299
+ continue;
6300
+ }
6301
+ if (expandedNodeIds.size >= resolved.maxResults) {
6302
+ truncated = true;
6303
+ frontierNodeIds.add(nextNode.id);
6304
+ continue;
6305
+ }
6306
+ visitedDepthByNodeId.set(nextNode.id, nextDepth);
6307
+ expandedNodeIds.add(nextNode.id);
6308
+ expandedEdgeIds.add(edge.id);
6309
+ if (nextDepth >= resolved.maxDepth) {
6310
+ frontierNodeIds.add(nextNode.id);
6311
+ continue;
6312
+ }
6313
+ queue.push({ nodeId: nextNode.id, depth: nextDepth });
6314
+ }
6315
+ }
6316
+ const expandedNodes = [...expandedNodeIds].map((nodeId2) => index.nodesById.get(nodeId2)).filter((node) => Boolean(node));
6317
+ const hiddenResourceNodes = expandedNodes.filter((node) => hiddenResourceNodeIds.has(node.id)).length;
6318
+ const alreadyVisibleNodes = expandedNodes.filter((node) => alreadyVisibleNodeIds.has(node.id)).length;
6319
+ const newNodes = expandedNodes.filter(
6320
+ (node) => !hiddenResourceNodeIds.has(node.id) && !alreadyVisibleNodeIds.has(node.id)
6321
+ ).length;
6322
+ const counts = {
6323
+ newNodes,
6324
+ newEdges: expandedEdgeIds.size,
6325
+ alreadyVisibleNodes,
6326
+ hiddenResourceNodes
6327
+ };
6328
+ const summaryMessages = buildSummaryMessages({
6329
+ rootNode,
6330
+ preset: resolved.preset,
6331
+ expandedNodes,
6332
+ counts,
6333
+ truncated
6334
+ });
6335
+ return {
6336
+ rootNodeId: request.rootNodeId,
6337
+ rootNode,
6338
+ preset: resolved.preset,
6339
+ direction: resolved.direction,
6340
+ maxDepth: resolved.maxDepth,
6341
+ maxResults: resolved.maxResults,
6342
+ expandedNodeIds: [...expandedNodeIds],
6343
+ expandedEdgeIds: [...expandedEdgeIds],
6344
+ frontierNodeIds: [...frontierNodeIds].filter((nodeId2) => nodeId2 !== resolved.rootNodeId),
6345
+ truncated,
6346
+ counts,
6347
+ summaryMessages,
6348
+ message: summaryMessages[0] ?? ""
6349
+ };
6350
+ }
6351
+ var PRESET_OPTIONS = [
6352
+ { value: "coverage", label: "Coverage" },
6353
+ { value: "operational-dependencies", label: "Operational dependencies" },
6354
+ { value: "org-context", label: "Org context" },
6355
+ { value: "impact-path", label: "Impact path" }
6356
+ ];
6357
+ var DIRECTION_OPTIONS = [
6358
+ { value: "both", label: "Both" },
6359
+ { value: "outgoing", label: "Outgoing" },
6360
+ { value: "incoming", label: "Incoming" }
6361
+ ];
6362
+ var EDGE_KIND_OPTIONS = [
6363
+ { value: "contains", label: "Containment" },
6364
+ { value: "references", label: "References" },
6365
+ { value: "exposes", label: "Exposes" },
6366
+ { value: "maps_to", label: "Maps to" },
6367
+ { value: "operates-on", label: "Operates on" },
6368
+ { value: "uses", label: "Uses" }
6369
+ ];
6370
+ var RELATIONSHIP_OPTIONS = [
6371
+ { value: "triggers", label: "Triggers" },
6372
+ { value: "uses", label: "Uses" },
6373
+ { value: "approval", label: "Approval" }
6374
+ ];
6375
+ var RESOURCE_TYPE_OPTIONS = [
6376
+ { value: "workflow", label: "Workflow" },
6377
+ { value: "agent", label: "Agent" },
6378
+ { value: "trigger", label: "Trigger" },
6379
+ { value: "integration", label: "Integration" },
6380
+ { value: "external", label: "External" },
6381
+ { value: "human_checkpoint", label: "Human checkpoint" }
6382
+ ];
6383
+ function updateValue(value, key, nextValue, onChange) {
6384
+ onChange({
6385
+ ...value,
6386
+ [key]: nextValue
6387
+ });
6388
+ }
6389
+ function toOptionalArray(values) {
6390
+ return values.length > 0 ? values : void 0;
6391
+ }
6392
+ function toMaxDepth(value) {
6393
+ const numeric = typeof value === "number" ? value : Number.parseInt(value, 10);
6394
+ if (numeric === 3) return 3;
6395
+ if (numeric === 2) return 2;
6396
+ return 1;
6397
+ }
6398
+ function ExpandAroundPanel({
6399
+ selectedNode,
6400
+ value,
6401
+ result,
6402
+ appliedNodeCount,
6403
+ appliedEdgeCount,
6404
+ onChange,
6405
+ onPreview,
6406
+ onApply,
6407
+ onClear
6408
+ }) {
6409
+ const disabled = !selectedNode;
6410
+ const canApply = Boolean(result && result.expandedNodeIds.length > 0);
6411
+ const rootIsTooBroad = selectedNode?.kind === "organization" && !value.preset;
6412
+ return /* @__PURE__ */ jsx(Paper, { withBorder: true, radius: "md", p: "sm", children: /* @__PURE__ */ jsxs(Stack, { gap: "sm", children: [
6413
+ /* @__PURE__ */ jsxs(Group, { justify: "space-between", align: "flex-start", wrap: "wrap", children: [
6414
+ /* @__PURE__ */ jsxs(Stack, { gap: 2, children: [
6415
+ /* @__PURE__ */ jsx(Text, { size: "sm", fw: 800, children: "Expand Around" }),
6416
+ /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: selectedNode ? `Preview typed graph context around ${selectedNode.label}.` : "Select a graph node to preview nearby operational context." })
6417
+ ] }),
6418
+ appliedNodeCount > 0 || appliedEdgeCount > 0 ? /* @__PURE__ */ jsxs(Badge, { variant: "light", color: "blue", children: [
6419
+ appliedNodeCount,
6420
+ " nodes / ",
6421
+ appliedEdgeCount,
6422
+ " edges applied"
6423
+ ] }) : null
6424
+ ] }),
6425
+ /* @__PURE__ */ jsxs(SimpleGrid, { cols: { base: 1, sm: 2 }, spacing: "sm", children: [
6426
+ /* @__PURE__ */ jsx(
6427
+ Select,
6428
+ {
6429
+ label: "Preset",
6430
+ placeholder: "Auto",
6431
+ data: PRESET_OPTIONS,
6432
+ value: value.preset ?? null,
6433
+ onChange: (preset) => updateValue(value, "preset", preset ?? void 0, onChange),
6434
+ disabled,
6435
+ clearable: true
6436
+ }
6437
+ ),
6438
+ /* @__PURE__ */ jsx(
6439
+ Select,
6440
+ {
6441
+ label: "Direction",
6442
+ data: DIRECTION_OPTIONS,
6443
+ value: value.direction ?? "both",
6444
+ onChange: (direction) => updateValue(value, "direction", direction ?? "both", onChange),
6445
+ disabled
6446
+ }
6447
+ ),
6448
+ /* @__PURE__ */ jsx(
6449
+ NumberInput,
6450
+ {
6451
+ label: "Depth",
6452
+ min: 1,
6453
+ max: 3,
6454
+ value: value.maxDepth ?? 1,
6455
+ onChange: (maxDepth) => updateValue(value, "maxDepth", toMaxDepth(maxDepth), onChange),
6456
+ disabled
6457
+ }
6458
+ ),
6459
+ /* @__PURE__ */ jsx(
6460
+ NumberInput,
6461
+ {
6462
+ label: "Max results",
6463
+ min: 1,
6464
+ max: 100,
6465
+ value: value.maxResults ?? 25,
6466
+ onChange: (maxResults) => updateValue(
6467
+ value,
6468
+ "maxResults",
6469
+ Math.max(1, typeof maxResults === "number" ? maxResults : Number.parseInt(maxResults, 10) || 25),
6470
+ onChange
6471
+ ),
6472
+ disabled
6473
+ }
6474
+ )
6475
+ ] }),
6476
+ /* @__PURE__ */ jsx(
6477
+ MultiSelect,
6478
+ {
6479
+ label: "Relationship filters",
6480
+ placeholder: "Preset defaults",
6481
+ data: RELATIONSHIP_OPTIONS,
6482
+ value: value.relationshipTypes ?? [],
6483
+ onChange: (relationshipTypes) => updateValue(value, "relationshipTypes", toOptionalArray(relationshipTypes), onChange),
6484
+ disabled,
6485
+ clearable: true
6486
+ }
6487
+ ),
6488
+ /* @__PURE__ */ jsx(
6489
+ MultiSelect,
6490
+ {
6491
+ label: "Edge kind filters",
6492
+ placeholder: "Preset defaults",
6493
+ data: EDGE_KIND_OPTIONS,
6494
+ value: value.edgeKinds ?? [],
6495
+ onChange: (edgeKinds) => updateValue(value, "edgeKinds", toOptionalArray(edgeKinds), onChange),
6496
+ disabled,
6497
+ clearable: true
6498
+ }
6499
+ ),
6500
+ /* @__PURE__ */ jsx(
6501
+ MultiSelect,
6502
+ {
6503
+ label: "Resource type filters",
6504
+ placeholder: "All resource types",
6505
+ data: RESOURCE_TYPE_OPTIONS,
6506
+ value: value.resourceTypes ?? [],
6507
+ onChange: (resourceTypes) => updateValue(value, "resourceTypes", toOptionalArray(resourceTypes), onChange),
6508
+ disabled,
6509
+ clearable: true
6510
+ }
6511
+ ),
6512
+ /* @__PURE__ */ jsx(
6513
+ Switch,
6514
+ {
6515
+ label: "Include hidden resources",
6516
+ checked: value.includeHiddenResources ?? true,
6517
+ onChange: (event) => updateValue(value, "includeHiddenResources", event.currentTarget.checked, onChange),
6518
+ disabled
6519
+ }
6520
+ ),
6521
+ rootIsTooBroad ? /* @__PURE__ */ jsx(Text, { size: "xs", c: "orange", children: "The organization root is broad. Choose a narrower node or a semantic preset before previewing." }) : null,
6522
+ result ? /* @__PURE__ */ jsx(Card, { withBorder: true, radius: "md", p: "sm", children: /* @__PURE__ */ jsxs(Stack, { gap: 6, children: [
6523
+ /* @__PURE__ */ jsxs(Group, { gap: "xs", wrap: "wrap", children: [
6524
+ /* @__PURE__ */ jsxs(Badge, { variant: "light", children: [
6525
+ result.counts.newNodes,
6526
+ " new"
6527
+ ] }),
6528
+ /* @__PURE__ */ jsxs(Badge, { variant: "light", children: [
6529
+ result.counts.alreadyVisibleNodes,
6530
+ " visible"
6531
+ ] }),
6532
+ /* @__PURE__ */ jsxs(Badge, { variant: "light", color: result.counts.hiddenResourceNodes > 0 ? "orange" : "gray", children: [
6533
+ result.counts.hiddenResourceNodes,
6534
+ " hidden"
6535
+ ] }),
6536
+ result.truncated ? /* @__PURE__ */ jsx(Badge, { variant: "light", color: "yellow", children: "Truncated" }) : null
6537
+ ] }),
6538
+ result.summaryMessages.map((message) => /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: message }, message))
6539
+ ] }) }) : null,
6540
+ /* @__PURE__ */ jsxs(Group, { gap: "xs", justify: "space-between", wrap: "wrap", children: [
6541
+ /* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
6542
+ /* @__PURE__ */ jsx(Button, { size: "xs", variant: "light", onClick: onPreview, disabled: disabled || rootIsTooBroad, children: "Preview" }),
6543
+ /* @__PURE__ */ jsx(Button, { size: "xs", onClick: onApply, disabled: !canApply, children: "Apply" })
6544
+ ] }),
6545
+ /* @__PURE__ */ jsx(Button, { size: "xs", variant: "subtle", onClick: onClear, disabled: !result && appliedNodeCount === 0, children: "Clear expansion" })
6546
+ ] })
6547
+ ] }) });
6548
+ }
5836
6549
  var cellStyle = {
5837
6550
  padding: "var(--mantine-spacing-sm) var(--mantine-spacing-md)",
5838
6551
  display: "flex",
@@ -6125,345 +6838,114 @@ function toResourceHealth(node, stats) {
6125
6838
  const summaryLabel2 = checkpointStats.pendingCount > 0 ? `${checkpointStats.pendingCount} pending` : checkpointStats.expiredCount > 0 ? `${checkpointStats.expiredCount} expired` : checkpointStats.completedCount > 0 ? `${checkpointStats.completedCount} completed` : "No queue activity";
6126
6839
  const detailLabel = checkpointStats.pendingCount > 0 ? `${checkpointStats.completedCount} resolved` : totalQueueActivity > 0 ? "Queue clear" : "No approvals in range";
6127
6840
  return {
6128
- nodeId: node.id,
6129
- resourceId,
6130
- label: node.label,
6131
- resourceType: node.resourceType,
6132
- healthLevel: healthLevel2,
6133
- summaryLabel: summaryLabel2,
6134
- detailLabel,
6135
- successRate: null,
6136
- totalRuns: 0,
6137
- successCount: 0,
6138
- failureCount: 0,
6139
- warningCount: 0,
6140
- pendingCount: checkpointStats.pendingCount,
6141
- completedCount: checkpointStats.completedCount,
6142
- expiredCount: checkpointStats.expiredCount,
6143
- lastActivityAt: checkpointStats.lastDecisionAt,
6144
- sortScore: checkpointStats.pendingCount * 100 + checkpointStats.expiredCount * 25 + checkpointStats.completedCount
6145
- };
6146
- }
6147
- const resourceStats = stats.resources[resourceId];
6148
- if (!resourceStats || resourceStats.totalRuns <= 0) {
6149
- return {
6150
- nodeId: node.id,
6151
- resourceId,
6152
- label: node.label,
6153
- resourceType: node.resourceType,
6154
- healthLevel: "inactive",
6155
- summaryLabel: "No runs",
6156
- detailLabel: "No executions in range",
6157
- successRate: null,
6158
- totalRuns: 0,
6159
- successCount: 0,
6160
- failureCount: 0,
6161
- warningCount: 0,
6162
- pendingCount: 0,
6163
- completedCount: 0,
6164
- expiredCount: 0,
6165
- lastActivityAt: resourceStats?.lastRunAt ?? null,
6166
- sortScore: 0
6167
- };
6168
- }
6169
- const successfulOutcomes = resourceStats.successCount + resourceStats.warningCount;
6170
- const successRate = successfulOutcomes / resourceStats.totalRuns * 100;
6171
- const healthLevel = successRate >= 95 ? "healthy" : successRate >= 80 ? "warning" : "critical";
6172
- const summaryLabel = resourceStats.failureCount > 0 ? `${resourceStats.failureCount} failed` : resourceStats.warningCount > 0 ? `${resourceStats.warningCount} warnings` : `${resourceStats.totalRuns} runs`;
6173
- return {
6174
- nodeId: node.id,
6175
- resourceId,
6176
- label: node.label,
6177
- resourceType: node.resourceType,
6178
- healthLevel,
6179
- summaryLabel,
6180
- detailLabel: `${Math.round(successRate)}% healthy`,
6181
- successRate,
6182
- totalRuns: resourceStats.totalRuns,
6183
- successCount: resourceStats.successCount,
6184
- failureCount: resourceStats.failureCount,
6185
- warningCount: resourceStats.warningCount,
6186
- pendingCount: 0,
6187
- completedCount: 0,
6188
- expiredCount: 0,
6189
- lastActivityAt: resourceStats.lastRunAt,
6190
- sortScore: resourceStats.failureCount * 100 + resourceStats.warningCount * 25 + resourceStats.totalRuns
6191
- };
6192
- }
6193
- function getCommandViewGraphNodeHealth(node, stats) {
6194
- if (node.kind !== "resource" || !stats) {
6195
- return null;
6196
- }
6197
- return toResourceHealth(node, stats);
6198
- }
6199
- function getCommandViewGraphHealthMap(graph, stats) {
6200
- const healthByNodeId = /* @__PURE__ */ new Map();
6201
- if (!graph || !stats) {
6202
- return healthByNodeId;
6203
- }
6204
- for (const node of graph.nodes) {
6205
- const health = getCommandViewGraphNodeHealth(node, stats);
6206
- if (health) {
6207
- healthByNodeId.set(node.id, health);
6208
- }
6209
- }
6210
- return healthByNodeId;
6211
- }
6212
- function getHealthRank(level) {
6213
- switch (level) {
6214
- case "critical":
6215
- return 0;
6216
- case "warning":
6217
- return 1;
6218
- case "healthy":
6219
- return 2;
6220
- case "inactive":
6221
- default:
6222
- return 3;
6223
- }
6224
- }
6225
- function rankCommandViewHotspots(entries, limit = 3) {
6226
- return [...entries].filter((entry) => entry.healthLevel === "critical" || entry.healthLevel === "warning").sort((left, right) => {
6227
- const healthRankDelta = getHealthRank(left.healthLevel) - getHealthRank(right.healthLevel);
6228
- if (healthRankDelta !== 0) {
6229
- return healthRankDelta;
6230
- }
6231
- if (right.sortScore !== left.sortScore) {
6232
- return right.sortScore - left.sortScore;
6233
- }
6234
- return left.label.localeCompare(right.label);
6235
- }).slice(0, limit);
6236
- }
6237
-
6238
- // src/features/operations/organization-graph/path-tracing/trace.ts
6239
- var NODE_KIND_ORDER = {
6240
- organization: 0,
6241
- feature: 1,
6242
- surface: 2,
6243
- capability: 3,
6244
- entity: 4,
6245
- resource: 5
6246
- };
6247
- var NODE_KIND_LABEL = {
6248
- organization: "Organization",
6249
- feature: "Feature",
6250
- surface: "Surface",
6251
- capability: "Capability",
6252
- entity: "Entity",
6253
- resource: "Resource"
6254
- };
6255
- function getNodeLabel(node) {
6256
- return node.label || node.sourceId || node.id;
6257
- }
6258
- function compareTraceNodes(a, b) {
6259
- const kindDelta = NODE_KIND_ORDER[a.kind] - NODE_KIND_ORDER[b.kind];
6260
- if (kindDelta !== 0) {
6261
- return kindDelta;
6262
- }
6263
- const labelDelta = getNodeLabel(a).localeCompare(getNodeLabel(b));
6264
- if (labelDelta !== 0) {
6265
- return labelDelta;
6266
- }
6267
- return a.id.localeCompare(b.id);
6268
- }
6269
- function buildMissingNodeMessage(selection, missingNodeIds) {
6270
- if (missingNodeIds.length === 0) {
6271
- return "The selected trace is not available.";
6272
- }
6273
- if (missingNodeIds.length === 1) {
6274
- const missingId = missingNodeIds[0];
6275
- return `Graph node "${missingId}" is not available.`;
6276
- }
6277
- return `Graph nodes "${selection.sourceId}" and "${selection.targetId}" are not available.`;
6278
- }
6279
- function buildPathNotFoundMessage(source, target) {
6280
- return `No directed path found from "${source.label}" to "${target.label}".`;
6281
- }
6282
- function buildOrganizationGraphTraceIndex(graph) {
6283
- const nodesById = /* @__PURE__ */ new Map();
6284
- const edgesById = /* @__PURE__ */ new Map();
6285
- const outgoingEdgesByNodeId = /* @__PURE__ */ new Map();
6286
- const incomingEdgesByNodeId = /* @__PURE__ */ new Map();
6287
- for (const node of graph.nodes) {
6288
- nodesById.set(node.id, node);
6289
- outgoingEdgesByNodeId.set(node.id, []);
6290
- incomingEdgesByNodeId.set(node.id, []);
6291
- }
6292
- for (const edge of graph.edges) {
6293
- edgesById.set(edge.id, edge);
6294
- const outgoingEdges = outgoingEdgesByNodeId.get(edge.sourceId);
6295
- if (outgoingEdges) {
6296
- outgoingEdges.push(edge);
6297
- }
6298
- const incomingEdges = incomingEdgesByNodeId.get(edge.targetId);
6299
- if (incomingEdges) {
6300
- incomingEdges.push(edge);
6301
- }
6302
- }
6303
- return {
6304
- nodesById,
6305
- edgesById,
6306
- outgoingEdgesByNodeId,
6307
- incomingEdgesByNodeId
6308
- };
6309
- }
6310
- function getOrganizationGraphTraceNodeKindLabel(kind) {
6311
- return NODE_KIND_LABEL[kind];
6312
- }
6313
- function formatOrganizationGraphTraceNodeOptionLabel(option) {
6314
- return `${option.label} \xB7 ${getOrganizationGraphTraceNodeKindLabel(option.kind)}`;
6315
- }
6316
- function getOrganizationGraphTraceNodeOptions(graph) {
6317
- return [...graph.nodes].sort(compareTraceNodes).map((node) => ({
6318
- id: node.id,
6319
- label: getNodeLabel(node),
6320
- kind: node.kind,
6321
- sourceId: node.sourceId,
6322
- description: node.description,
6323
- enabled: node.enabled
6324
- }));
6325
- }
6326
- function resolveOrganizationGraphPathTrace(graph, selection) {
6327
- const index = buildOrganizationGraphTraceIndex(graph);
6328
- const source = selection.sourceId ? index.nodesById.get(selection.sourceId) ?? null : null;
6329
- const target = selection.targetId ? index.nodesById.get(selection.targetId) ?? null : null;
6330
- const missingNodeIds = [
6331
- selection.sourceId && !source ? selection.sourceId : null,
6332
- selection.targetId && !target ? selection.targetId : null
6333
- ].filter((value) => Boolean(value));
6334
- if (!selection.sourceId && !selection.targetId) {
6335
- return {
6336
- status: "idle",
6337
- selection,
6338
- source,
6339
- target,
6340
- missingNodeIds: [],
6341
- pathNodes: [],
6342
- pathEdges: [],
6343
- highlightNodeIds: [],
6344
- highlightEdgeIds: [],
6345
- distance: 0,
6346
- message: "Select a source and target to trace a path."
6347
- };
6348
- }
6349
- if (!selection.sourceId || !selection.targetId) {
6350
- return {
6351
- status: "incomplete",
6352
- selection,
6353
- source,
6354
- target,
6355
- missingNodeIds,
6356
- pathNodes: [],
6357
- pathEdges: [],
6358
- highlightNodeIds: [],
6359
- highlightEdgeIds: [],
6360
- distance: 0,
6361
- message: "Select both a source and a target to resolve a path."
6841
+ nodeId: node.id,
6842
+ resourceId,
6843
+ label: node.label,
6844
+ resourceType: node.resourceType,
6845
+ healthLevel: healthLevel2,
6846
+ summaryLabel: summaryLabel2,
6847
+ detailLabel,
6848
+ successRate: null,
6849
+ totalRuns: 0,
6850
+ successCount: 0,
6851
+ failureCount: 0,
6852
+ warningCount: 0,
6853
+ pendingCount: checkpointStats.pendingCount,
6854
+ completedCount: checkpointStats.completedCount,
6855
+ expiredCount: checkpointStats.expiredCount,
6856
+ lastActivityAt: checkpointStats.lastDecisionAt,
6857
+ sortScore: checkpointStats.pendingCount * 100 + checkpointStats.expiredCount * 25 + checkpointStats.completedCount
6362
6858
  };
6363
6859
  }
6364
- if (missingNodeIds.length > 0 || !source || !target) {
6860
+ const resourceStats = stats.resources[resourceId];
6861
+ if (!resourceStats || resourceStats.totalRuns <= 0) {
6365
6862
  return {
6366
- status: "missing-node",
6367
- selection,
6368
- source,
6369
- target,
6370
- missingNodeIds,
6371
- pathNodes: [],
6372
- pathEdges: [],
6373
- highlightNodeIds: [],
6374
- highlightEdgeIds: [],
6375
- distance: 0,
6376
- message: buildMissingNodeMessage(selection, missingNodeIds)
6863
+ nodeId: node.id,
6864
+ resourceId,
6865
+ label: node.label,
6866
+ resourceType: node.resourceType,
6867
+ healthLevel: "inactive",
6868
+ summaryLabel: "No runs",
6869
+ detailLabel: "No executions in range",
6870
+ successRate: null,
6871
+ totalRuns: 0,
6872
+ successCount: 0,
6873
+ failureCount: 0,
6874
+ warningCount: 0,
6875
+ pendingCount: 0,
6876
+ completedCount: 0,
6877
+ expiredCount: 0,
6878
+ lastActivityAt: resourceStats?.lastRunAt ?? null,
6879
+ sortScore: 0
6377
6880
  };
6378
6881
  }
6379
- if (source.id === target.id) {
6380
- return {
6381
- status: "found",
6382
- selection,
6383
- source,
6384
- target,
6385
- missingNodeIds: [],
6386
- pathNodes: [source],
6387
- pathEdges: [],
6388
- highlightNodeIds: [source.id],
6389
- highlightEdgeIds: [],
6390
- distance: 0,
6391
- message: `Source and target already point to "${source.label}".`
6392
- };
6882
+ const successfulOutcomes = resourceStats.successCount + resourceStats.warningCount;
6883
+ const successRate = successfulOutcomes / resourceStats.totalRuns * 100;
6884
+ const healthLevel = successRate >= 95 ? "healthy" : successRate >= 80 ? "warning" : "critical";
6885
+ const summaryLabel = resourceStats.failureCount > 0 ? `${resourceStats.failureCount} failed` : resourceStats.warningCount > 0 ? `${resourceStats.warningCount} warnings` : `${resourceStats.totalRuns} runs`;
6886
+ return {
6887
+ nodeId: node.id,
6888
+ resourceId,
6889
+ label: node.label,
6890
+ resourceType: node.resourceType,
6891
+ healthLevel,
6892
+ summaryLabel,
6893
+ detailLabel: `${Math.round(successRate)}% healthy`,
6894
+ successRate,
6895
+ totalRuns: resourceStats.totalRuns,
6896
+ successCount: resourceStats.successCount,
6897
+ failureCount: resourceStats.failureCount,
6898
+ warningCount: resourceStats.warningCount,
6899
+ pendingCount: 0,
6900
+ completedCount: 0,
6901
+ expiredCount: 0,
6902
+ lastActivityAt: resourceStats.lastRunAt,
6903
+ sortScore: resourceStats.failureCount * 100 + resourceStats.warningCount * 25 + resourceStats.totalRuns
6904
+ };
6905
+ }
6906
+ function getCommandViewGraphNodeHealth(node, stats) {
6907
+ if (node.kind !== "resource" || !stats) {
6908
+ return null;
6393
6909
  }
6394
- const visitedNodeIds = /* @__PURE__ */ new Set([source.id]);
6395
- const predecessorByNodeId = /* @__PURE__ */ new Map();
6396
- const queue = [source.id];
6397
- while (queue.length > 0) {
6398
- const currentNodeId = queue.shift();
6399
- if (!currentNodeId) {
6400
- continue;
6401
- }
6402
- const outgoingEdges = index.outgoingEdgesByNodeId.get(currentNodeId) ?? [];
6403
- for (const edge of outgoingEdges) {
6404
- if (visitedNodeIds.has(edge.targetId)) {
6405
- continue;
6406
- }
6407
- visitedNodeIds.add(edge.targetId);
6408
- predecessorByNodeId.set(edge.targetId, {
6409
- nodeId: currentNodeId,
6410
- edgeId: edge.id
6411
- });
6412
- if (edge.targetId === target.id) {
6413
- queue.length = 0;
6414
- break;
6415
- }
6416
- queue.push(edge.targetId);
6910
+ return toResourceHealth(node, stats);
6911
+ }
6912
+ function getCommandViewGraphHealthMap(graph, stats) {
6913
+ const healthByNodeId = /* @__PURE__ */ new Map();
6914
+ if (!graph || !stats) {
6915
+ return healthByNodeId;
6916
+ }
6917
+ for (const node of graph.nodes) {
6918
+ const health = getCommandViewGraphNodeHealth(node, stats);
6919
+ if (health) {
6920
+ healthByNodeId.set(node.id, health);
6417
6921
  }
6418
6922
  }
6419
- if (!predecessorByNodeId.has(target.id)) {
6420
- return {
6421
- status: "not-found",
6422
- selection,
6423
- source,
6424
- target,
6425
- missingNodeIds: [],
6426
- pathNodes: [],
6427
- pathEdges: [],
6428
- highlightNodeIds: [],
6429
- highlightEdgeIds: [],
6430
- distance: 0,
6431
- message: buildPathNotFoundMessage(source, target)
6432
- };
6923
+ return healthByNodeId;
6924
+ }
6925
+ function getHealthRank(level) {
6926
+ switch (level) {
6927
+ case "critical":
6928
+ return 0;
6929
+ case "warning":
6930
+ return 1;
6931
+ case "healthy":
6932
+ return 2;
6933
+ case "inactive":
6934
+ default:
6935
+ return 3;
6433
6936
  }
6434
- const pathNodes = [target];
6435
- const pathEdges = [];
6436
- let cursorNodeId = target.id;
6437
- while (cursorNodeId !== source.id) {
6438
- const predecessor = predecessorByNodeId.get(cursorNodeId);
6439
- if (!predecessor) {
6440
- break;
6441
- }
6442
- const edge = index.edgesById.get(predecessor.edgeId);
6443
- const previousNode = index.nodesById.get(predecessor.nodeId);
6444
- if (edge) {
6445
- pathEdges.push(edge);
6937
+ }
6938
+ function rankCommandViewHotspots(entries, limit = 3) {
6939
+ return [...entries].filter((entry) => entry.healthLevel === "critical" || entry.healthLevel === "warning").sort((left, right) => {
6940
+ const healthRankDelta = getHealthRank(left.healthLevel) - getHealthRank(right.healthLevel);
6941
+ if (healthRankDelta !== 0) {
6942
+ return healthRankDelta;
6446
6943
  }
6447
- if (previousNode) {
6448
- pathNodes.push(previousNode);
6944
+ if (right.sortScore !== left.sortScore) {
6945
+ return right.sortScore - left.sortScore;
6449
6946
  }
6450
- cursorNodeId = predecessor.nodeId;
6451
- }
6452
- pathNodes.reverse();
6453
- pathEdges.reverse();
6454
- return {
6455
- status: "found",
6456
- selection,
6457
- source,
6458
- target,
6459
- missingNodeIds: [],
6460
- pathNodes,
6461
- pathEdges,
6462
- highlightNodeIds: pathNodes.map((node) => node.id),
6463
- highlightEdgeIds: pathEdges.map((edge) => edge.id),
6464
- distance: pathEdges.length,
6465
- message: `Path found from "${source.label}" to "${target.label}" across ${pathEdges.length} edge${pathEdges.length === 1 ? "" : "s"}.`
6466
- };
6947
+ return left.label.localeCompare(right.label);
6948
+ }).slice(0, limit);
6467
6949
  }
6468
6950
  var STATUS_LABELS = {
6469
6951
  idle: "Idle",
@@ -6587,6 +7069,14 @@ var EMPTY_TRACE_SELECTION = {
6587
7069
  };
6588
7070
  var EMPTY_GRAPH_HIDDEN_IDS = /* @__PURE__ */ new Set();
6589
7071
  var EMPTY_GRAPH_HIDDEN_EDGE_IDS = /* @__PURE__ */ new Set();
7072
+ var EMPTY_EXPANDED_NODE_IDS = /* @__PURE__ */ new Set();
7073
+ var EMPTY_EXPANDED_EDGE_IDS = /* @__PURE__ */ new Set();
7074
+ var DEFAULT_EXPAND_AROUND_VALUE = {
7075
+ direction: "both",
7076
+ maxDepth: 1,
7077
+ maxResults: 25,
7078
+ includeHiddenResources: true
7079
+ };
6590
7080
  var FALLBACK_GRAPH_THEME = {
6591
7081
  primary: "#4a6e8e",
6592
7082
  background: "#030507",
@@ -6994,13 +7484,31 @@ function createCytoscapeStyle(tokens) {
6994
7484
  "target-arrow-color": traceBorder,
6995
7485
  "line-style": "solid"
6996
7486
  }
7487
+ },
7488
+ {
7489
+ selector: "node.is-expanded-node",
7490
+ style: {
7491
+ opacity: 1,
7492
+ "border-color": mixColors(tokens.primary, tokens.warning, 0.72),
7493
+ "border-width": 3.4,
7494
+ "background-opacity": 1
7495
+ }
7496
+ },
7497
+ {
7498
+ selector: "edge.is-expanded-edge",
7499
+ style: {
7500
+ opacity: 1,
7501
+ width: 3.4,
7502
+ "line-opacity": 0.94,
7503
+ label: "data(label)"
7504
+ }
6997
7505
  }
6998
7506
  ];
6999
7507
  }
7000
- function syncGraphClasses(cy, selectedElement, traceResult, hiddenIds, hiddenEdgeIds) {
7508
+ function syncGraphClasses(cy, selectedElement, traceResult, hiddenIds, hiddenEdgeIds, expandedNodeIds, expandedEdgeIds) {
7001
7509
  cy.batch(() => {
7002
7510
  cy.elements().removeClass(
7003
- "is-hidden is-faded is-context is-selected is-connected is-trace-node is-trace-edge is-trace-endpoint"
7511
+ "is-hidden is-faded is-context is-selected is-connected is-trace-node is-trace-edge is-trace-endpoint is-expanded-node is-expanded-edge"
7004
7512
  );
7005
7513
  for (const nodeId2 of hiddenIds) {
7006
7514
  const node = cy.getElementById(nodeId2);
@@ -7018,9 +7526,33 @@ function syncGraphClasses(cy, selectedElement, traceResult, hiddenIds, hiddenEdg
7018
7526
  traceResult && (traceResult.highlightNodeIds.length > 0 || traceResult.highlightEdgeIds.length > 0)
7019
7527
  );
7020
7528
  if (!selectedElement && !hasTrace) {
7529
+ for (const nodeId2 of expandedNodeIds) {
7530
+ const node = cy.getElementById(nodeId2);
7531
+ if (!node.empty()) {
7532
+ node.addClass("is-expanded-node");
7533
+ }
7534
+ }
7535
+ for (const edgeId2 of expandedEdgeIds) {
7536
+ const edge2 = cy.getElementById(edgeId2);
7537
+ if (!edge2.empty()) {
7538
+ edge2.addClass("is-expanded-edge");
7539
+ }
7540
+ }
7021
7541
  return;
7022
7542
  }
7023
7543
  cy.elements().addClass("is-faded");
7544
+ for (const nodeId2 of expandedNodeIds) {
7545
+ const node = cy.getElementById(nodeId2);
7546
+ if (!node.empty()) {
7547
+ node.removeClass("is-faded").addClass("is-context is-expanded-node");
7548
+ }
7549
+ }
7550
+ for (const edgeId2 of expandedEdgeIds) {
7551
+ const edge2 = cy.getElementById(edgeId2);
7552
+ if (!edge2.empty()) {
7553
+ edge2.removeClass("is-faded").addClass("is-connected is-expanded-edge");
7554
+ }
7555
+ }
7024
7556
  if (traceResult) {
7025
7557
  for (const nodeId2 of traceResult.highlightNodeIds) {
7026
7558
  const node = cy.getElementById(nodeId2);
@@ -7097,6 +7629,8 @@ function OrganizationGraphCanvas({
7097
7629
  traceResult,
7098
7630
  hiddenIds,
7099
7631
  hiddenEdgeIds,
7632
+ expandedNodeIds,
7633
+ expandedEdgeIds,
7100
7634
  themeTokens,
7101
7635
  commandViewHealthByNodeId,
7102
7636
  focusRequest,
@@ -7158,7 +7692,7 @@ function OrganizationGraphCanvas({
7158
7692
  });
7159
7693
  });
7160
7694
  resizeObserver.observe(containerRef.current);
7161
- syncGraphClasses(cy, selectedElement, traceResult, hiddenIds, hiddenEdgeIds);
7695
+ syncGraphClasses(cy, selectedElement, traceResult, hiddenIds, hiddenEdgeIds, expandedNodeIds, expandedEdgeIds);
7162
7696
  return () => {
7163
7697
  resizeObserver.disconnect();
7164
7698
  cy.destroy();
@@ -7172,7 +7706,7 @@ function OrganizationGraphCanvas({
7172
7706
  }
7173
7707
  syncCytoscapeElements(cy, elements);
7174
7708
  cy.layout(getLayoutOptions(mode, selectedElement, traceResult)).run();
7175
- syncGraphClasses(cy, selectedElement, traceResult, hiddenIds, hiddenEdgeIds);
7709
+ syncGraphClasses(cy, selectedElement, traceResult, hiddenIds, hiddenEdgeIds, expandedNodeIds, expandedEdgeIds);
7176
7710
  }, [elements]);
7177
7711
  useEffect(() => {
7178
7712
  const cy = cytoscapeRef.current;
@@ -7191,7 +7725,7 @@ function OrganizationGraphCanvas({
7191
7725
  if (!cy) {
7192
7726
  return;
7193
7727
  }
7194
- syncGraphClasses(cy, selectedElement, traceResult, hiddenIds, hiddenEdgeIds);
7728
+ syncGraphClasses(cy, selectedElement, traceResult, hiddenIds, hiddenEdgeIds, expandedNodeIds, expandedEdgeIds);
7195
7729
  if (mode === "map" && selectedElement?.type === "node") {
7196
7730
  const node = cy.getElementById(selectedElement.id);
7197
7731
  if (!node.empty()) {
@@ -7211,7 +7745,7 @@ function OrganizationGraphCanvas({
7211
7745
  );
7212
7746
  }
7213
7747
  }
7214
- }, [hiddenEdgeIds, hiddenIds, mode, selectedElement, traceResult]);
7748
+ }, [expandedEdgeIds, expandedNodeIds, hiddenEdgeIds, hiddenIds, mode, selectedElement, traceResult]);
7215
7749
  useEffect(() => {
7216
7750
  const cy = cytoscapeRef.current;
7217
7751
  if (!cy || !focusRequest) {
@@ -7354,6 +7888,10 @@ function OrganizationGraphPage({ lens = "default", timeRange = "30d" }) {
7354
7888
  const [selectedElement, setSelectedElement] = useState(null);
7355
7889
  const [focusRequest, setFocusRequest] = useState(null);
7356
7890
  const [pathTraceSelection, setPathTraceSelection] = useState(EMPTY_TRACE_SELECTION);
7891
+ const [expandAroundValue, setExpandAroundValue] = useState(DEFAULT_EXPAND_AROUND_VALUE);
7892
+ const [expandAroundPreview, setExpandAroundPreview] = useState(null);
7893
+ const [appliedExpandAroundNodeIds, setAppliedExpandAroundNodeIds] = useState(EMPTY_EXPANDED_NODE_IDS);
7894
+ const [appliedExpandAroundEdgeIds, setAppliedExpandAroundEdgeIds] = useState(EMPTY_EXPANDED_EDGE_IDS);
7357
7895
  const { filters, resetFilters, updateFilters } = useOrganizationGraphFilters(lensConfig.initialFilters);
7358
7896
  const toolbarResetValue = useMemo(
7359
7897
  () => createOrganizationGraphFilters(lensConfig.initialFilters),
@@ -7385,6 +7923,12 @@ function OrganizationGraphPage({ lens = "default", timeRange = "30d" }) {
7385
7923
  commandViewData
7386
7924
  });
7387
7925
  }, [baseGraph, commandViewData, deferredFilters]);
7926
+ const commandViewRevealIds = useMemo(() => {
7927
+ if (appliedExpandAroundNodeIds.size === 0) {
7928
+ return revealedIds;
7929
+ }
7930
+ return /* @__PURE__ */ new Set([...revealedIds, ...appliedExpandAroundNodeIds]);
7931
+ }, [appliedExpandAroundNodeIds, revealedIds]);
7388
7932
  const visibilityProjection = useMemo(() => {
7389
7933
  if (!graph || lens !== "command-view") {
7390
7934
  const resourceCount = getGraphCountByKind(graph, "resource");
@@ -7403,10 +7947,10 @@ function OrganizationGraphPage({ lens = "default", timeRange = "30d" }) {
7403
7947
  resourcesHidden,
7404
7948
  diagnosticsHidden,
7405
7949
  diagnosticCategories,
7406
- revealedIds,
7950
+ revealedIds: commandViewRevealIds,
7407
7951
  mode
7408
7952
  });
7409
- }, [commandViewData, diagnosticCategories, diagnosticsHidden, graph, lens, mode, resourcesHidden, revealedIds]);
7953
+ }, [commandViewData, commandViewRevealIds, diagnosticCategories, diagnosticsHidden, graph, lens, mode, resourcesHidden]);
7410
7954
  const visibleGraph = useMemo(() => {
7411
7955
  if (!graph || visibilityProjection.hiddenIds.size === 0) {
7412
7956
  return graph;
@@ -7489,6 +8033,43 @@ function OrganizationGraphPage({ lens = "default", timeRange = "30d" }) {
7489
8033
  nonce: Date.now()
7490
8034
  });
7491
8035
  });
8036
+ const clearExpandAroundState = useEffectEvent(() => {
8037
+ setExpandAroundPreview(null);
8038
+ setAppliedExpandAroundNodeIds(EMPTY_EXPANDED_NODE_IDS);
8039
+ setAppliedExpandAroundEdgeIds(EMPTY_EXPANDED_EDGE_IDS);
8040
+ });
8041
+ const previewExpandAround = useEffectEvent(() => {
8042
+ if (lens !== "command-view" || !graph || selectedElement?.type !== "node") {
8043
+ setExpandAroundPreview(null);
8044
+ return;
8045
+ }
8046
+ setExpandAroundPreview(
8047
+ expandAroundGraph(
8048
+ graph,
8049
+ {
8050
+ rootNodeId: selectedElement.id,
8051
+ ...expandAroundValue
8052
+ },
8053
+ {
8054
+ alreadyVisibleNodeIds: visibleGraph?.nodes.map((node) => node.id) ?? [],
8055
+ hiddenResourceNodeIds: visibilityProjection.hiddenIds
8056
+ }
8057
+ )
8058
+ );
8059
+ });
8060
+ const applyExpandAroundPreview = useEffectEvent(() => {
8061
+ if (!expandAroundPreview || selectedElement?.type !== "node") {
8062
+ return;
8063
+ }
8064
+ setAppliedExpandAroundNodeIds(new Set(expandAroundPreview.expandedNodeIds));
8065
+ setAppliedExpandAroundEdgeIds(new Set(expandAroundPreview.expandedEdgeIds));
8066
+ setMode("map");
8067
+ setSelectedElement({ type: "node", id: selectedElement.id });
8068
+ focusGraphNode(selectedElement.id);
8069
+ if (expandAroundPreview.counts.hiddenResourceNodes > 0) {
8070
+ markVisibilityInteraction();
8071
+ }
8072
+ });
7492
8073
  const focusCommandViewResource = useEffectEvent((resourceId) => {
7493
8074
  const focusNode = visibleGraph?.nodes.find((node) => node.kind === "resource" && node.sourceId === resourceId);
7494
8075
  if (!focusNode) {
@@ -7501,6 +8082,7 @@ function OrganizationGraphPage({ lens = "default", timeRange = "30d" }) {
7501
8082
  const handleResetFilters = useEffectEvent(() => {
7502
8083
  resetFilters();
7503
8084
  clearRevealedIds();
8085
+ clearExpandAroundState();
7504
8086
  setSelectedElement(null);
7505
8087
  setPathTraceSelection(EMPTY_TRACE_SELECTION);
7506
8088
  });
@@ -7508,6 +8090,7 @@ function OrganizationGraphPage({ lens = "default", timeRange = "30d" }) {
7508
8090
  setResourcesHidden(value);
7509
8091
  if (value) {
7510
8092
  clearRevealedIds();
8093
+ clearExpandAroundState();
7511
8094
  setSelectedElement(null);
7512
8095
  }
7513
8096
  });
@@ -7515,20 +8098,23 @@ function OrganizationGraphPage({ lens = "default", timeRange = "30d" }) {
7515
8098
  setDiagnosticsHidden(value);
7516
8099
  if (value) {
7517
8100
  clearRevealedIds();
8101
+ clearExpandAroundState();
7518
8102
  }
7519
8103
  });
7520
8104
  const revealAllResources = useEffectEvent(() => {
7521
8105
  setResourcesHidden(false);
7522
8106
  setDiagnosticsHidden(false);
7523
8107
  clearRevealedIds();
8108
+ clearExpandAroundState();
7524
8109
  });
7525
8110
  const handleSelectElement = useEffectEvent((element) => {
7526
8111
  if (!element) {
7527
- if (!selectedElement && revealedIds.size === 0) {
8112
+ if (!selectedElement && revealedIds.size === 0 && appliedExpandAroundNodeIds.size === 0) {
7528
8113
  return;
7529
8114
  }
7530
8115
  setSelectedElement(null);
7531
8116
  clearRevealedIds();
8117
+ clearExpandAroundState();
7532
8118
  return;
7533
8119
  }
7534
8120
  if (mode === "map" && element.type === "node" && selectedElement?.type === "node" && selectedElement.id === element.id) {
@@ -7543,7 +8129,9 @@ function OrganizationGraphPage({ lens = "default", timeRange = "30d" }) {
7543
8129
  }
7544
8130
  } else {
7545
8131
  clearRevealedIds();
8132
+ clearExpandAroundState();
7546
8133
  }
8134
+ setExpandAroundPreview(null);
7547
8135
  setSelectedElement(element);
7548
8136
  });
7549
8137
  useEffect(() => {
@@ -7554,8 +8142,19 @@ function OrganizationGraphPage({ lens = "default", timeRange = "30d" }) {
7554
8142
  if (!elementExists) {
7555
8143
  setSelectedElement(null);
7556
8144
  clearRevealedIds();
8145
+ clearExpandAroundState();
7557
8146
  }
7558
- }, [clearRevealedIds, selectedElement, visibleGraph]);
8147
+ }, [clearExpandAroundState, clearRevealedIds, selectedElement, visibleGraph]);
8148
+ useEffect(() => {
8149
+ if (lens !== "command-view" || !graph || appliedExpandAroundNodeIds.size === 0) {
8150
+ return;
8151
+ }
8152
+ const graphNodeIds = new Set(graph.nodes.map((node) => node.id));
8153
+ const expansionStillExists = [...appliedExpandAroundNodeIds].some((nodeId2) => graphNodeIds.has(nodeId2));
8154
+ if (!expansionStillExists) {
8155
+ clearExpandAroundState();
8156
+ }
8157
+ }, [appliedExpandAroundNodeIds, clearExpandAroundState, graph, lens]);
7559
8158
  useEffect(() => {
7560
8159
  if (lens !== "command-view") {
7561
8160
  return;
@@ -7658,6 +8257,8 @@ function OrganizationGraphPage({ lens = "default", timeRange = "30d" }) {
7658
8257
  traceResult: activeTraceResult,
7659
8258
  hiddenIds: EMPTY_GRAPH_HIDDEN_IDS,
7660
8259
  hiddenEdgeIds: EMPTY_GRAPH_HIDDEN_EDGE_IDS,
8260
+ expandedNodeIds: lens === "command-view" ? appliedExpandAroundNodeIds : EMPTY_EXPANDED_NODE_IDS,
8261
+ expandedEdgeIds: lens === "command-view" ? appliedExpandAroundEdgeIds : EMPTY_EXPANDED_EDGE_IDS,
7661
8262
  themeTokens,
7662
8263
  commandViewHealthByNodeId,
7663
8264
  focusRequest,
@@ -7897,9 +8498,27 @@ function OrganizationGraphPage({ lens = "default", timeRange = "30d" }) {
7897
8498
  selectedElement,
7898
8499
  supplementalSummary: selectionOperationalSummary,
7899
8500
  followUpSections,
8501
+ expandAroundPanel: lens === "command-view" ? /* @__PURE__ */ jsx(
8502
+ ExpandAroundPanel,
8503
+ {
8504
+ selectedNode: selectedGraphNode,
8505
+ value: expandAroundValue,
8506
+ result: expandAroundPreview,
8507
+ appliedNodeCount: appliedExpandAroundNodeIds.size,
8508
+ appliedEdgeCount: appliedExpandAroundEdgeIds.size,
8509
+ onChange: (next) => {
8510
+ setExpandAroundValue(next);
8511
+ setExpandAroundPreview(null);
8512
+ },
8513
+ onPreview: previewExpandAround,
8514
+ onApply: applyExpandAroundPreview,
8515
+ onClear: clearExpandAroundState
8516
+ }
8517
+ ) : null,
7900
8518
  onClearSelection: () => {
7901
8519
  setSelectedElement(null);
7902
8520
  clearRevealedIds();
8521
+ clearExpandAroundState();
7903
8522
  }
7904
8523
  }
7905
8524
  ) }),