@bufinance/gtm-graph 0.1.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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/view/index.ts","../../src/view/gtm-graph-view.tsx","../../src/algorithms/graph-paths.ts","../../src/algorithms/gtm-decorations.ts","../../src/view/physics.ts","../../src/view/theme.ts","../../src/view/view-model.ts"],"sourcesContent":["export { GtmGraphView, type GtmGraphViewProps } from './gtm-graph-view';\nexport {\n CLASS_ACCENTS,\n EDGE_TYPE_STYLES,\n type EdgeTypeStyle,\n ENTITY_COLORS,\n type EntityColor,\n edgeTypeStyle,\n entityColor,\n FALLBACK_ENTITY_COLOR,\n nodeRadius,\n TRACE_ACCENTS,\n} from './theme';\nexport {\n buildEdgeVisuals,\n buildNodeVisuals,\n buildTrace,\n type EdgeVisual,\n type NodeVisual,\n resolveClasses,\n type TraceResult,\n traceEdgeIds,\n} from './view-model';\n","'use client';\n\n/**\n * GtmGraphView — force-directed GTM account map (plan 100).\n *\n * View adapted from the BUFI desk repo's bubble-memory constellation\n * (`packages/intelligence-ui/src/bubble-memory/bubble-memory-view.tsx`) with\n * GTM overlays: POI/bridge node classes, recency-faded edges, pair-pick\n * warm-path tracing (strongest vs fewest), search, legend, and a node cap\n * with expand. Self-contained: physics copied (see ./physics.ts header),\n * inline styling only, zero `@bu/*` imports.\n */\n\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react';\n\nimport { capGraph } from '../algorithms';\nimport type { GtmGraphData, GtmGraphDecorations } from '../types';\nimport { getFloatingOffset, type PhysicsEdge, type PhysicsNode, runSimulation } from './physics';\nimport { CLASS_ACCENTS, ENTITY_COLORS, TRACE_ACCENTS } from './theme';\nimport {\n buildEdgeVisuals,\n buildNodeVisuals,\n buildTrace,\n resolveClasses,\n type TraceResult,\n traceEdgeIds,\n} from './view-model';\n\nexport interface GtmGraphViewProps {\n data: GtmGraphData;\n /** Server-computed decorations; missing pieces are computed client-side. */\n decorations?: Partial<GtmGraphDecorations>;\n /** Epoch ms driving recency fading + path weights (default: Date.now() at mount). */\n now?: number;\n /** Render at most this many nodes (highest-degree first); an Expand control shows the rest. Default 400. */\n maxNodes?: number;\n /** How many top-betweenness nodes get the bridge accent. Default 5. */\n bridgeTopN?: number;\n /** Node click callback — hosts own the detail UI. */\n onEntitySelect?: (entityId: string) => void;\n /** Viewport height in px (width is fluid). Default 560. */\n height?: number;\n /** Dark theme toggle — hosts pass their theme state. Default false. */\n dark?: boolean;\n}\n\ninterface Positions {\n map: Map<string, { x: number; y: number }>;\n}\n\nconst SIM_WIDTH = 1200;\nconst SIM_HEIGHT = 800;\n\nexport function GtmGraphView({\n data,\n decorations,\n now,\n maxNodes = 400,\n bridgeTopN = 5,\n onEntitySelect,\n height = 560,\n dark = false,\n}: GtmGraphViewProps) {\n const [expanded, setExpanded] = useState(false);\n const [searchQuery, setSearchQuery] = useState('');\n const [traceMode, setTraceMode] = useState(false);\n const [tracePick, setTracePick] = useState<string[]>([]);\n const [hoveredId, setHoveredId] = useState<string | null>(null);\n const [pan, setPan] = useState({ x: 0, y: 0 });\n const [zoom, setZoom] = useState(0.9);\n const [motionTick, setMotionTick] = useState(0);\n const containerRef = useRef<HTMLDivElement>(null);\n const panningRef = useRef<{\n startX: number;\n startY: number;\n originX: number;\n originY: number;\n } | null>(null);\n const motionTimeRef = useRef(0);\n const nowRef = useRef(now ?? Date.now());\n const effectiveNow = now ?? nowRef.current;\n\n // --- graph slicing (cap + expand) -----------------------------------------\n const { poiIds, bridgeIds } = useMemo(\n () => resolveClasses(data, decorations, { bridgeTopN, now: effectiveNow }),\n [data, decorations, bridgeTopN, effectiveNow]\n );\n\n const pinned = useMemo(() => new Set([...poiIds, ...bridgeIds]), [poiIds, bridgeIds]);\n const visible = useMemo(\n () => (expanded ? data : (capGraph(data, maxNodes, pinned) as GtmGraphData)),\n [data, expanded, maxNodes, pinned]\n );\n const cappedCount = data.entities.length - visible.entities.length;\n\n const nodeVisuals = useMemo(\n () => buildNodeVisuals(visible, poiIds, bridgeIds, searchQuery),\n [visible, poiIds, bridgeIds, searchQuery]\n );\n const edgeVisuals = useMemo(\n () => buildEdgeVisuals(visible, effectiveNow),\n [visible, effectiveNow]\n );\n const nodeById = useMemo(() => new Map(nodeVisuals.map(n => [n.id, n])), [nodeVisuals]);\n\n // --- layout ----------------------------------------------------------------\n const positions = useMemo<Positions>(() => {\n const physicsNodes: PhysicsNode[] = nodeVisuals.map(n => ({\n id: n.id,\n x: 0,\n y: 0,\n vx: 0,\n vy: 0,\n radius: n.radius,\n group: n.entityType,\n }));\n const physicsEdges: PhysicsEdge[] = edgeVisuals.map(e => ({\n source: e.sourceEntityId,\n target: e.targetEntityId,\n strength: e.springStrength,\n }));\n runSimulation(physicsNodes, physicsEdges, SIM_WIDTH, SIM_HEIGHT);\n return { map: new Map(physicsNodes.map(n => [n.id, { x: n.x, y: n.y }])) };\n }, [nodeVisuals, edgeVisuals]);\n\n // Gentle floating motion (client only)\n useEffect(() => {\n let rafId = 0;\n let last = typeof performance !== 'undefined' ? performance.now() : 0;\n const animate = (time: number) => {\n if (time - last >= 48) {\n motionTimeRef.current += time - last;\n last = time;\n setMotionTick(t => t + 1);\n }\n rafId = requestAnimationFrame(animate);\n };\n if (typeof requestAnimationFrame !== 'undefined') rafId = requestAnimationFrame(animate);\n return () => {\n if (rafId && typeof cancelAnimationFrame !== 'undefined') cancelAnimationFrame(rafId);\n };\n }, []);\n\n // Center pan once we can measure\n useEffect(() => {\n const el = containerRef.current;\n if (!el) return;\n setPan({ x: el.clientWidth / 2, y: height / 2 });\n }, [height]);\n\n const displayPosition = useCallback(\n (id: string) => {\n const base = positions.map.get(id);\n if (!base) return null;\n const offset = getFloatingOffset(id, motionTimeRef.current);\n return { x: base.x + offset.dx, y: base.y + offset.dy };\n },\n // motionTick forces recompute as the float clock advances\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [positions, motionTick]\n );\n\n // --- trace (pair pick → strongest vs fewest) --------------------------------\n const trace: TraceResult | null = useMemo(() => {\n if (tracePick.length !== 2) return null;\n return buildTrace(visible, tracePick[0]!, tracePick[1]!, effectiveNow);\n }, [tracePick, visible, effectiveNow]);\n const traced = useMemo(() => traceEdgeIds(trace), [trace]);\n\n const handleNodeClick = useCallback(\n (entityId: string) => {\n if (traceMode) {\n setTracePick(prev => {\n if (prev.includes(entityId)) return prev.filter(id => id !== entityId);\n if (prev.length >= 2) return [prev[1]!, entityId];\n return [...prev, entityId];\n });\n return;\n }\n onEntitySelect?.(entityId);\n },\n [traceMode, onEntitySelect]\n );\n\n // --- pan/zoom ---------------------------------------------------------------\n const handlePointerDown = useCallback(\n (event: React.PointerEvent) => {\n if (event.button !== 0) return;\n event.currentTarget.setPointerCapture(event.pointerId);\n panningRef.current = {\n startX: event.clientX,\n startY: event.clientY,\n originX: pan.x,\n originY: pan.y,\n };\n },\n [pan]\n );\n const handlePointerMove = useCallback((event: React.PointerEvent) => {\n const panning = panningRef.current;\n if (!panning) return;\n setPan({\n x: panning.originX + (event.clientX - panning.startX),\n y: panning.originY + (event.clientY - panning.startY),\n });\n }, []);\n const handlePointerUp = useCallback(() => {\n panningRef.current = null;\n }, []);\n const handleWheel = useCallback((event: React.WheelEvent) => {\n const factor = Math.exp(-event.deltaY * 0.0022);\n setZoom(z => Math.min(2.5, Math.max(0.25, z * factor)));\n }, []);\n\n // --- palette ------------------------------------------------------------------\n const bg = dark ? '#0b1020' : '#fbfaff';\n const text = dark ? '#c6bbff' : '#333336';\n const subText = dark ? '#8f86c4' : '#87839c';\n const panelBg = dark ? 'rgba(15,20,45,0.9)' : 'rgba(255,255,255,0.92)';\n const panelBorder = dark ? 'rgba(130,140,255,0.25)' : '#e2d0fd';\n\n const hovered = hoveredId ? nodeById.get(hoveredId) : null;\n const legendTypes = useMemo(() => {\n const present = new Set(nodeVisuals.map(n => n.entityType));\n return Object.entries(ENTITY_COLORS).filter(([type]) => present.has(type));\n }, [nodeVisuals]);\n\n const traceChain = (path: { nodeIds: string[]; edgeIds: string[] } | null) => {\n if (!path) return 'no path';\n return path.nodeIds\n .map((id, i) => {\n const name = nodeById.get(id)?.entityName ?? id.slice(0, 6);\n if (i === path.nodeIds.length - 1) return name;\n const edge = edgeVisuals.find(e => e.id === path.edgeIds[i]);\n return `${name} —${edge ? `${edge.label} ×${edge.evidenceCount}` : '?'}→ `;\n })\n .join('');\n };\n\n return (\n <div\n ref={containerRef}\n style={{\n position: 'relative',\n width: '100%',\n height,\n overflow: 'hidden',\n borderRadius: 12,\n border: `1px solid ${panelBorder}`,\n background: bg,\n fontFamily: 'Poppins, ui-sans-serif, system-ui, sans-serif',\n color: text,\n }}\n data-testid=\"gtm-graph-view\"\n >\n {/* Controls */}\n <div\n style={{\n position: 'absolute',\n top: 10,\n left: 10,\n zIndex: 20,\n display: 'flex',\n gap: 8,\n alignItems: 'center',\n }}\n >\n <input\n value={searchQuery}\n onChange={e => setSearchQuery(e.target.value)}\n placeholder=\"Search entities…\"\n aria-label=\"Search entities\"\n style={{\n padding: '6px 10px',\n fontSize: 12,\n borderRadius: 8,\n border: `1px solid ${panelBorder}`,\n background: panelBg,\n color: text,\n outline: 'none',\n width: 180,\n }}\n />\n <button\n type=\"button\"\n onClick={() => {\n setTraceMode(m => !m);\n setTracePick([]);\n }}\n style={{\n padding: '6px 10px',\n fontSize: 12,\n borderRadius: 8,\n cursor: 'pointer',\n border: `1px solid ${traceMode ? CLASS_ACCENTS.poi : panelBorder}`,\n background: traceMode ? CLASS_ACCENTS.poi : panelBg,\n color: traceMode ? '#fff' : text,\n }}\n >\n {traceMode\n ? `Trace: pick ${2 - Math.min(tracePick.length, 2)} node${tracePick.length === 1 ? '' : 's'}`\n : 'Trace warm path'}\n </button>\n {!expanded && cappedCount > 0 && (\n <button\n type=\"button\"\n onClick={() => setExpanded(true)}\n style={{\n padding: '6px 10px',\n fontSize: 12,\n borderRadius: 8,\n cursor: 'pointer',\n border: `1px solid ${panelBorder}`,\n background: panelBg,\n color: text,\n }}\n >\n Expand +{cappedCount} nodes\n </button>\n )}\n </div>\n\n {/* Graph canvas */}\n {/* biome-ignore lint/a11y/noSvgWithoutTitle: decorative interactive canvas, labelled by the container */}\n <svg\n width=\"100%\"\n height=\"100%\"\n style={{ touchAction: 'none', cursor: panningRef.current ? 'grabbing' : 'grab' }}\n onPointerDown={handlePointerDown}\n onPointerMove={handlePointerMove}\n onPointerUp={handlePointerUp}\n onWheel={handleWheel}\n role=\"img\"\n aria-label=\"GTM graph\"\n >\n <g transform={`translate(${pan.x} ${pan.y}) scale(${zoom})`}>\n {edgeVisuals.map(edge => {\n const source = displayPosition(edge.sourceEntityId);\n const target = displayPosition(edge.targetEntityId);\n if (!source || !target) return null;\n const onStrongest = traced.strongest.has(edge.id);\n const onFewest = traced.fewest.has(edge.id);\n const isTraced = onStrongest || onFewest;\n const dimmedByTrace = trace !== null && !isTraced;\n const srcColor = nodeById.get(edge.sourceEntityId)?.stroke ?? '#818CF8';\n const strokeColor = onStrongest\n ? TRACE_ACCENTS.strongest\n : onFewest\n ? TRACE_ACCENTS.fewest\n : srcColor;\n const opacity = dimmedByTrace ? 0.04 : isTraced ? 0.95 : edge.opacity;\n const width = isTraced ? 2.4 : 0.6 + edge.springStrength * 1.2;\n return (\n <line\n key={edge.id}\n x1={source.x}\n y1={source.y}\n x2={target.x}\n y2={target.y}\n stroke={strokeColor}\n strokeOpacity={opacity}\n strokeWidth={width}\n strokeLinecap=\"round\"\n strokeDasharray={isTraced ? undefined : edge.dash}\n data-testid={`gtm-edge-${edge.id}`}\n data-traced={isTraced ? (onStrongest ? 'strongest' : 'fewest') : undefined}\n />\n );\n })}\n\n {nodeVisuals.map(node => {\n const pos = displayPosition(node.id);\n if (!pos) return null;\n const isHovered = hoveredId === node.id;\n const isPicked = tracePick.includes(node.id);\n const onTracePath = traced.nodes.has(node.id);\n let opacity = node.matched ? 1 : 0.12;\n if (trace !== null) opacity = onTracePath || isPicked ? 1 : 0.15;\n return (\n // biome-ignore lint/a11y/useKeyWithClickEvents: canvas nodes; hosts provide accessible detail UI via onEntitySelect\n <g\n key={node.id}\n transform={`translate(${pos.x}, ${pos.y})`}\n opacity={opacity}\n style={{ cursor: 'pointer', transition: 'opacity 0.25s' }}\n onClick={() => handleNodeClick(node.id)}\n onPointerEnter={() => setHoveredId(node.id)}\n onPointerLeave={() => setHoveredId(null)}\n data-testid={`gtm-node-${node.id}`}\n data-node-class={node.nodeClass}\n >\n <circle r={node.radius + 7} fill={node.glow} opacity={isHovered ? 0.35 : 0.12} />\n {node.ring && (\n <circle\n r={node.radius + 3.5}\n fill=\"none\"\n stroke={node.ring}\n strokeWidth={2}\n strokeDasharray={node.nodeClass === 'bridge' ? '3 2' : undefined}\n />\n )}\n {isPicked && (\n <circle\n r={node.radius + 7.5}\n fill=\"none\"\n stroke={TRACE_ACCENTS.endpoints}\n strokeWidth={2.5}\n />\n )}\n <circle\n r={node.radius}\n fill={node.fill}\n stroke={isHovered ? '#fff' : node.stroke}\n strokeWidth={isHovered ? 2 : 0.8}\n />\n {(isHovered || node.nodeClass !== 'other' || zoom > 1.3) && (\n <text\n y={node.radius + 14}\n textAnchor=\"middle\"\n style={{\n fontSize: 10,\n fill: subText,\n pointerEvents: 'none',\n userSelect: 'none',\n }}\n >\n {node.entityName.length > 24\n ? `${node.entityName.slice(0, 23)}…`\n : node.entityName}\n </text>\n )}\n </g>\n );\n })}\n </g>\n </svg>\n\n {/* Hover card */}\n {hovered && (\n <div\n style={{\n position: 'absolute',\n bottom: 12,\n left: 12,\n zIndex: 20,\n padding: '8px 12px',\n borderRadius: 10,\n border: `1px solid ${panelBorder}`,\n background: panelBg,\n fontSize: 12,\n maxWidth: 280,\n pointerEvents: 'none',\n }}\n >\n <div style={{ fontWeight: 600 }}>{hovered.entityName}</div>\n <div style={{ color: subText }}>\n {hovered.entityType}\n {hovered.nodeClass !== 'other' && (\n <span style={{ color: hovered.ring ?? text, fontWeight: 600 }}>\n {' '}\n · {hovered.nodeClass === 'poi' ? 'person of interest' : 'bridge'}\n </span>\n )}\n </div>\n </div>\n )}\n\n {/* Trace result panel */}\n {trace && (\n <div\n data-testid=\"gtm-trace-panel\"\n style={{\n position: 'absolute',\n bottom: 12,\n right: 12,\n zIndex: 20,\n padding: '10px 12px',\n borderRadius: 10,\n border: `1px solid ${panelBorder}`,\n background: panelBg,\n fontSize: 11,\n maxWidth: 380,\n }}\n >\n <div style={{ fontWeight: 600, marginBottom: 4 }}>\n Warm path {trace.diverges ? '(strongest ≠ fewest)' : ''}\n </div>\n <div style={{ color: TRACE_ACCENTS.strongest, marginBottom: 2 }}>\n Strongest: {traceChain(trace.strongest)}\n </div>\n <div style={{ color: TRACE_ACCENTS.fewest }}>Fewest hops: {traceChain(trace.fewest)}</div>\n </div>\n )}\n\n {/* Legend */}\n <div\n data-testid=\"gtm-legend\"\n style={{\n position: 'absolute',\n top: 10,\n right: 10,\n zIndex: 20,\n padding: '8px 12px',\n borderRadius: 10,\n border: `1px solid ${panelBorder}`,\n background: panelBg,\n fontSize: 11,\n display: 'flex',\n flexDirection: 'column',\n gap: 4,\n }}\n >\n {legendTypes.map(([type, color]) => (\n <div key={type} style={{ display: 'flex', alignItems: 'center', gap: 6 }}>\n <span\n style={{\n width: 9,\n height: 9,\n borderRadius: '50%',\n background: color.fill,\n border: `1px solid ${color.stroke}`,\n }}\n />\n <span style={{ color: subText }}>{color.label}</span>\n </div>\n ))}\n <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>\n <span\n style={{\n width: 9,\n height: 9,\n borderRadius: '50%',\n border: `2px solid ${CLASS_ACCENTS.poi}`,\n }}\n />\n <span style={{ color: subText }}>Person of interest</span>\n </div>\n <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>\n <span\n style={{\n width: 9,\n height: 9,\n borderRadius: '50%',\n border: `2px dashed ${CLASS_ACCENTS.bridge}`,\n }}\n />\n <span style={{ color: subText }}>Bridge</span>\n </div>\n </div>\n\n {/* Empty state */}\n {visible.entities.length === 0 && (\n <div\n style={{\n position: 'absolute',\n inset: 0,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n color: subText,\n fontSize: 13,\n }}\n >\n No GTM entities yet — the graph fills as the sync ingests leads, deals, and activities.\n </div>\n )}\n </div>\n );\n}\n","/**\n * Graph path / bridge engine (plan 099; re-homed here as the published SSOT\n * in plan 100 — `packages/intelligence/src/knowledge/graph-paths.ts` is now a\n * re-export shim of this module).\n *\n * PURE module: no I/O, no clocks, no randomness — every time- or\n * order-sensitive function takes explicit `now` / `seed` / `pairs` inputs so\n * results are deterministic and testable, and so the UI can run the same\n * code client-side over a fetched graph payload.\n *\n * Operates on a camelCase graph shape structurally compatible with the\n * `get_knowledge_graph` RPC payload (`KnowledgeGraphData` in the BUFI desk\n * repo). NOTE: that RPC's relationship objects omit `lastSeenAt`; the weight\n * formula treats a missing `lastSeenAt` as \"fresh\" (factor 1).\n *\n * Weight formula (documented constants below):\n * w = strength * ln(1 + evidenceCount) * recencyFactor(lastSeenAt, now)\n * recencyFactor = 0.5 ^ (daysStale / 90) — halves per 90 stale days\n *\n * Scale ceiling: everything here is in-memory over the fetched graph —\n * fine for the BUFI-internal GTM graph (thousands of edges). Past ~50k\n * edges, graduate to pgRouting / an RPC (see the 2026-07-16 gtm-graph spec).\n */\n\n// ---------------------------------------------------------------------------\n// Structural input types (compatible with KnowledgeGraphData and the raw\n// get_knowledge_graph JSON — only the fields used here are required)\n// ---------------------------------------------------------------------------\n\nexport interface GraphEntityLike {\n id: string;\n}\n\nexport interface GraphEdgeLike {\n id: string;\n sourceEntityId: string;\n targetEntityId: string;\n relationshipType: string;\n strength?: number | null;\n evidenceCount?: number | null;\n lastSeenAt?: string | null;\n}\n\nexport interface GraphDataLike {\n entities: GraphEntityLike[];\n relationships: GraphEdgeLike[];\n}\n\nexport interface AdjacentEdge {\n edgeId: string;\n otherId: string;\n relationshipType: string;\n weight: number;\n}\n\n/** node id → undirected incident edges (both directions materialized). */\nexport type Adjacency = Map<string, AdjacentEdge[]>;\n\nexport interface GraphPath {\n /** Node ids from source to target (inclusive). */\n nodeIds: string[];\n /** Edge ids traversed, in order (`nodeIds.length - 1` entries). */\n edgeIds: string[];\n /** Hop count (= edgeIds.length). */\n hops: number;\n /** Sum of Dijkstra costs (1/w) — lower = stronger; 0 for the trivial path. */\n cost: number;\n}\n\n// ---------------------------------------------------------------------------\n// Weight constants (documented; see module header)\n// ---------------------------------------------------------------------------\n\n/** Staleness half-life: an edge unseen for this many days weighs half. */\nexport const RECENCY_HALF_LIFE_DAYS = 90;\n/** Default strength when the payload omits it (DB default is 1.0). */\nexport const DEFAULT_STRENGTH = 1.0;\n/** Default evidence count when the payload omits it (DB default is 1). */\nexport const DEFAULT_EVIDENCE_COUNT = 1;\n/** Floor so degenerate edges never divide by zero in 1/w costs. */\nexport const MIN_EDGE_WEIGHT = 1e-6;\n\nconst MS_PER_DAY = 86_400_000;\n\n/** 0.5^(daysStale/90); missing/unparseable lastSeenAt → 1 (fresh). */\nexport function recencyFactor(lastSeenAt: string | null | undefined, now: number): number {\n if (!lastSeenAt) return 1;\n const seenAt = Date.parse(lastSeenAt);\n if (Number.isNaN(seenAt)) return 1;\n const daysStale = Math.max(0, (now - seenAt) / MS_PER_DAY);\n return 0.5 ** (daysStale / RECENCY_HALF_LIFE_DAYS);\n}\n\n/** w = strength * ln(1 + evidenceCount) * recencyFactor — floored at MIN_EDGE_WEIGHT. */\nexport function edgeWeight(edge: GraphEdgeLike, now: number): number {\n const strength = edge.strength ?? DEFAULT_STRENGTH;\n const evidence = edge.evidenceCount ?? DEFAULT_EVIDENCE_COUNT;\n const weight =\n strength * Math.log(1 + Math.max(0, evidence)) * recencyFactor(edge.lastSeenAt, now);\n return Math.max(MIN_EDGE_WEIGHT, weight);\n}\n\n// ---------------------------------------------------------------------------\n// buildAdjacency\n// ---------------------------------------------------------------------------\n\n/**\n * Undirected adjacency view with per-edge weights.\n * `now` (epoch ms) drives the recency factor — pass a fixed value in tests.\n */\nexport function buildAdjacency(data: GraphDataLike, options: { now: number }): Adjacency {\n const adjacency: Adjacency = new Map();\n for (const entity of data.entities) adjacency.set(entity.id, []);\n\n for (const edge of data.relationships) {\n const weight = edgeWeight(edge, options.now);\n const forward: AdjacentEdge = {\n edgeId: edge.id,\n otherId: edge.targetEntityId,\n relationshipType: edge.relationshipType,\n weight,\n };\n const backward: AdjacentEdge = {\n edgeId: edge.id,\n otherId: edge.sourceEntityId,\n relationshipType: edge.relationshipType,\n weight,\n };\n // Skip edges pointing at nodes missing from the entity list (partial fetch)\n if (adjacency.has(edge.sourceEntityId) && adjacency.has(edge.targetEntityId)) {\n adjacency.get(edge.sourceEntityId)!.push(forward);\n adjacency.get(edge.targetEntityId)!.push(backward);\n }\n }\n return adjacency;\n}\n\n// ---------------------------------------------------------------------------\n// fewestHops — BFS\n// ---------------------------------------------------------------------------\n\nexport interface FewestHopsOptions {\n now?: number;\n /** Give up beyond this many hops (default: unbounded). */\n maxHops?: number;\n /** Reuse a prebuilt adjacency (must match `data`). */\n adjacency?: Adjacency;\n}\n\n/** BFS shortest path by hop count. Null when unreachable (or beyond maxHops). */\nexport function fewestHops(\n data: GraphDataLike,\n fromId: string,\n toId: string,\n options: FewestHopsOptions = {}\n): GraphPath | null {\n const adjacency = options.adjacency ?? buildAdjacency(data, { now: options.now ?? 0 });\n if (!adjacency.has(fromId) || !adjacency.has(toId)) return null;\n if (fromId === toId) return { nodeIds: [fromId], edgeIds: [], hops: 0, cost: 0 };\n\n const previous = new Map<string, { nodeId: string; edge: AdjacentEdge }>();\n const visited = new Set<string>([fromId]);\n let frontier = [fromId];\n let depth = 0;\n\n while (frontier.length > 0) {\n depth++;\n if (options.maxHops != null && depth > options.maxHops) return null;\n const next: string[] = [];\n for (const nodeId of frontier) {\n for (const edge of adjacency.get(nodeId) ?? []) {\n if (visited.has(edge.otherId)) continue;\n visited.add(edge.otherId);\n previous.set(edge.otherId, { nodeId, edge });\n if (edge.otherId === toId) return reconstructPath(previous, fromId, toId);\n next.push(edge.otherId);\n }\n }\n frontier = next;\n }\n return null;\n}\n\nfunction reconstructPath(\n previous: Map<string, { nodeId: string; edge: AdjacentEdge }>,\n fromId: string,\n toId: string\n): GraphPath {\n const nodeIds: string[] = [toId];\n const edgeIds: string[] = [];\n let cost = 0;\n let cursor = toId;\n while (cursor !== fromId) {\n const step = previous.get(cursor)!;\n edgeIds.unshift(step.edge.edgeId);\n cost += 1 / step.edge.weight;\n nodeIds.unshift(step.nodeId);\n cursor = step.nodeId;\n }\n return { nodeIds, edgeIds, hops: edgeIds.length, cost };\n}\n\n// ---------------------------------------------------------------------------\n// strongestPath — Dijkstra on 1/w costs\n// ---------------------------------------------------------------------------\n\nexport interface StrongestPathOptions {\n /** Epoch ms for the recency factor. REQUIRED for determinism. */\n now: number;\n adjacency?: Adjacency;\n}\n\n/**\n * Dijkstra minimizing sum(1/w): prefers few, strong, recent edges.\n * Can legitimately return MORE hops than `fewestHops` when a longer chain of\n * strong edges beats a weak direct edge. Null when unreachable.\n */\nexport function strongestPath(\n data: GraphDataLike,\n fromId: string,\n toId: string,\n options: StrongestPathOptions\n): GraphPath | null {\n const adjacency = options.adjacency ?? buildAdjacency(data, { now: options.now });\n if (!adjacency.has(fromId) || !adjacency.has(toId)) return null;\n if (fromId === toId) return { nodeIds: [fromId], edgeIds: [], hops: 0, cost: 0 };\n\n const distance = new Map<string, number>([[fromId, 0]]);\n const previous = new Map<string, { nodeId: string; edge: AdjacentEdge }>();\n const settled = new Set<string>();\n // Simple O(V^2) selection — fine at the in-memory scale ceiling.\n const pending = new Set<string>([fromId]);\n\n while (pending.size > 0) {\n let current: string | null = null;\n let best = Number.POSITIVE_INFINITY;\n for (const nodeId of pending) {\n const d = distance.get(nodeId) ?? Number.POSITIVE_INFINITY;\n if (d < best) {\n best = d;\n current = nodeId;\n }\n }\n if (current == null) break;\n pending.delete(current);\n if (current === toId) break;\n if (settled.has(current)) continue;\n settled.add(current);\n\n for (const edge of adjacency.get(current) ?? []) {\n if (settled.has(edge.otherId)) continue;\n const candidate = best + 1 / edge.weight;\n if (candidate < (distance.get(edge.otherId) ?? Number.POSITIVE_INFINITY)) {\n distance.set(edge.otherId, candidate);\n previous.set(edge.otherId, { nodeId: current, edge });\n pending.add(edge.otherId);\n }\n }\n }\n\n if (!distance.has(toId)) return null;\n return reconstructPath(previous, fromId, toId);\n}\n\n// ---------------------------------------------------------------------------\n// bridgeScores — sampled approximate betweenness\n// ---------------------------------------------------------------------------\n\nexport interface BridgeScoreOptions {\n now?: number;\n /** Max sampled node pairs (default 200). */\n sampleSize?: number;\n /** PRNG seed — same seed + same graph → identical scores. */\n seed?: number;\n /** Explicit pair list overrides sampling entirely (fully deterministic). */\n pairs?: Array<[string, string]>;\n}\n\n/** Deterministic mulberry32 PRNG. */\nfunction mulberry32(seed: number): () => number {\n let a = seed >>> 0;\n return () => {\n a = (a + 0x6d2b79f5) | 0;\n let t = Math.imul(a ^ (a >>> 15), 1 | a);\n t ^= t + Math.imul(t ^ (t >>> 7), 61 | t);\n return ((t ^ (t >>> 14)) >>> 0) / 4294967296;\n };\n}\n\nexport const DEFAULT_BRIDGE_SAMPLE_SIZE = 200;\nexport const DEFAULT_BRIDGE_SEED = 99;\n\n/**\n * Approximate betweenness: run `fewestHops` over sampled node pairs and count\n * how often each node sits STRICTLY INSIDE the path. Normalized to [0, 1]\n * by the max count. Deterministic given (`seed` | `pairs`) + the graph.\n */\nexport function bridgeScores(\n data: GraphDataLike,\n options: BridgeScoreOptions = {}\n): Map<string, number> {\n const now = options.now ?? 0;\n const adjacency = buildAdjacency(data, { now });\n const nodeIds = data.entities.map(e => e.id);\n const scores = new Map<string, number>(nodeIds.map(id => [id, 0]));\n if (nodeIds.length < 3) return scores;\n\n let pairs: Array<[string, string]>;\n if (options.pairs) {\n pairs = options.pairs;\n } else {\n const sampleSize = options.sampleSize ?? DEFAULT_BRIDGE_SAMPLE_SIZE;\n const random = mulberry32(options.seed ?? DEFAULT_BRIDGE_SEED);\n const seen = new Set<string>();\n pairs = [];\n const maxDistinctPairs = (nodeIds.length * (nodeIds.length - 1)) / 2;\n const target = Math.min(sampleSize, maxDistinctPairs);\n // Bounded attempts so tiny graphs can't loop forever\n for (let attempt = 0; attempt < target * 20 && pairs.length < target; attempt++) {\n const i = Math.floor(random() * nodeIds.length);\n const j = Math.floor(random() * nodeIds.length);\n if (i === j) continue;\n const key = i < j ? `${i}|${j}` : `${j}|${i}`;\n if (seen.has(key)) continue;\n seen.add(key);\n pairs.push([nodeIds[Math.min(i, j)]!, nodeIds[Math.max(i, j)]!]);\n }\n }\n\n for (const [fromId, toId] of pairs) {\n const path = fewestHops(data, fromId, toId, { adjacency });\n if (!path) continue;\n // Strictly-inside nodes only\n for (const nodeId of path.nodeIds.slice(1, -1)) {\n scores.set(nodeId, (scores.get(nodeId) ?? 0) + 1);\n }\n }\n\n let max = 0;\n for (const score of scores.values()) if (score > max) max = score;\n if (max > 0) {\n for (const [nodeId, score] of scores) scores.set(nodeId, score / max);\n }\n return scores;\n}\n\n// ---------------------------------------------------------------------------\n// neighborhood — k-hop subgraph extraction\n// ---------------------------------------------------------------------------\n\n/**\n * The sub-graph within `hops` of `centerId` (entities + every edge whose two\n * endpoints are both inside). Empty graph when the center is unknown.\n */\nexport function neighborhood(data: GraphDataLike, centerId: string, hops: number): GraphDataLike {\n const adjacency = buildAdjacency(data, { now: 0 });\n if (!adjacency.has(centerId)) return { entities: [], relationships: [] };\n\n const included = new Set<string>([centerId]);\n let frontier = [centerId];\n for (let depth = 0; depth < hops && frontier.length > 0; depth++) {\n const next: string[] = [];\n for (const nodeId of frontier) {\n for (const edge of adjacency.get(nodeId) ?? []) {\n if (included.has(edge.otherId)) continue;\n included.add(edge.otherId);\n next.push(edge.otherId);\n }\n }\n frontier = next;\n }\n\n return {\n entities: data.entities.filter(e => included.has(e.id)),\n relationships: data.relationships.filter(\n r => included.has(r.sourceEntityId) && included.has(r.targetEntityId)\n ),\n };\n}\n","/**\n * GTM decorations — pure classification helpers on top of the path engine\n * (plan 100). Implements the POI + bridge definitions from the 2026-07-16\n * gtm-graph spec so every consumer (desk admin route, open-agents endpoint,\n * Shiva MCP routes) classifies nodes identically.\n *\n * - POI (person of interest): persons with `champion_of` /\n * `decision_maker_for` edges into OPEN deals, OR a decision-maker title\n * match on their metadata.\n * - Bridge: top-N nodes by sampled betweenness (`bridgeScores`) — the\n * people/orgs the most shortest paths flow through.\n */\n\nimport { bridgeScores } from './graph-paths';\n\n// ---------------------------------------------------------------------------\n// Structural inputs — a superset of graph-paths' *Like types: decorations\n// also need entity type/metadata and edge metadata.\n// ---------------------------------------------------------------------------\n\nexport interface GtmEntityLike {\n id: string;\n entityType: string;\n entityName?: string;\n metadata?: Record<string, unknown> | null;\n}\n\nexport interface GtmEdgeLike {\n id: string;\n sourceEntityId: string;\n targetEntityId: string;\n relationshipType: string;\n strength?: number | null;\n evidenceCount?: number | null;\n lastSeenAt?: string | null;\n metadata?: Record<string, unknown> | null;\n}\n\nexport interface GtmGraphLike {\n entities: GtmEntityLike[];\n relationships: GtmEdgeLike[];\n}\n\nexport type GtmNodeClass = 'poi' | 'bridge' | 'other';\n\n/** Edge types that mark a person as tied to a deal's buying decision. */\nexport const POI_EDGE_TYPES = ['champion_of', 'decision_maker_for'] as const;\n\n/**\n * Canonical CLOSED pipeline stages (lowercased): deals in these stages no\n * longer make their champions POIs. Mirrors `@bu/types/sales-pipeline`\n * (`Activo` = won, `Nurture/Perdido` = lost) — duplicated here because this\n * package is published standalone and cannot import `@bu/*`.\n */\nexport const DEFAULT_CLOSED_STAGES = ['activo', 'nurture/perdido', 'won', 'lost'] as const;\n\n/**\n * Decision-maker title fragments (lowercased substring match) for the\n * title-based POI rule. Derived from the current LATAM-agencies ICP\n * (tasks/notes/2026-07-15-icp-enriched-r1.md in the desk repo). Hosts can\n * override via `PoiOptions.titlePatterns`.\n */\nexport const DEFAULT_POI_TITLE_PATTERNS = [\n 'founder',\n 'co-founder',\n 'cofounder',\n 'ceo',\n 'coo',\n 'cfo',\n 'owner',\n 'partner',\n 'managing director',\n 'director general',\n 'general manager',\n 'head of finance',\n 'head of operations',\n 'finance manager',\n 'operations manager',\n] as const;\n\nexport interface PoiOptions {\n /** Lowercased stage names counting as CLOSED (default: DEFAULT_CLOSED_STAGES). */\n closedStages?: readonly string[];\n /** Lowercased title fragments that qualify a person as POI (default: DEFAULT_POI_TITLE_PATTERNS). */\n titlePatterns?: readonly string[];\n}\n\nfunction metadataString(\n metadata: Record<string, unknown> | null | undefined,\n key: string\n): string | null {\n const value = metadata?.[key];\n return typeof value === 'string' ? value : null;\n}\n\n/** True when the deal entity's `metadata.stage` is NOT in the closed set (unknown stage → open). */\nexport function isOpenDeal(\n deal: GtmEntityLike,\n closedStages: readonly string[] = DEFAULT_CLOSED_STAGES\n): boolean {\n const stage = metadataString(deal.metadata, 'stage');\n if (!stage) return true;\n return !closedStages.includes(stage.trim().toLowerCase());\n}\n\n/**\n * POI ids per the spec: persons with champion_of/decision_maker_for edges\n * into OPEN deals, OR a decision-maker title match.\n */\nexport function computePoiIds(data: GtmGraphLike, options: PoiOptions = {}): Set<string> {\n const closedStages = options.closedStages ?? DEFAULT_CLOSED_STAGES;\n const titlePatterns = options.titlePatterns ?? DEFAULT_POI_TITLE_PATTERNS;\n\n const byId = new Map(data.entities.map(e => [e.id, e]));\n const pois = new Set<string>();\n\n for (const edge of data.relationships) {\n if (!POI_EDGE_TYPES.includes(edge.relationshipType as (typeof POI_EDGE_TYPES)[number]))\n continue;\n const source = byId.get(edge.sourceEntityId);\n const target = byId.get(edge.targetEntityId);\n if (!source || source.entityType !== 'person' || !target) continue;\n // decision_maker_for may point at a deal OR an org; champion_of points at a deal.\n const isDealTarget = target.entityType === 'project';\n if (isDealTarget && !isOpenDeal(target, closedStages)) continue;\n pois.add(source.id);\n }\n\n for (const entity of data.entities) {\n if (entity.entityType !== 'person') continue;\n const title = metadataString(entity.metadata, 'title')?.toLowerCase();\n if (!title) continue;\n if (titlePatterns.some(pattern => title.includes(pattern))) pois.add(entity.id);\n }\n\n return pois;\n}\n\nexport interface BridgeOptions {\n /** How many top-scored nodes count as bridges (default 5). */\n topN?: number;\n /** Epoch ms for recency weighting (default 0 = ignore recency). */\n now?: number;\n /** Deterministic sampling seed forwarded to bridgeScores. */\n seed?: number;\n /** Explicit pair list forwarded to bridgeScores (fully deterministic). */\n pairs?: Array<[string, string]>;\n}\n\n/**\n * Top-N bridge node ids by sampled betweenness. Nodes with score 0 are never\n * bridges, so small/star graphs can return fewer than `topN`.\n */\nexport function computeBridgeIds(data: GtmGraphLike, options: BridgeOptions = {}): Set<string> {\n const topN = options.topN ?? 5;\n const scores = bridgeScores(data, {\n now: options.now ?? 0,\n seed: options.seed,\n pairs: options.pairs,\n });\n const ranked = [...scores.entries()]\n .filter(([, score]) => score > 0)\n .sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0]))\n .slice(0, topN);\n return new Set(ranked.map(([id]) => id));\n}\n\n/** Node class from precomputed POI/bridge sets — POI wins over bridge. */\nexport function classifyNode(\n id: string,\n poiIds: ReadonlySet<string>,\n bridgeIds: ReadonlySet<string>\n): GtmNodeClass {\n if (poiIds.has(id)) return 'poi';\n if (bridgeIds.has(id)) return 'bridge';\n return 'other';\n}\n\n/**\n * Cap a graph at `maxNodes` keeping the highest-degree nodes (POIs/bridges\n * always survive), plus every edge whose two endpoints survive.\n */\nexport function capGraph(\n data: GtmGraphLike,\n maxNodes: number,\n pinned: ReadonlySet<string> = new Set()\n): GtmGraphLike {\n if (data.entities.length <= maxNodes) return data;\n\n const degree = new Map<string, number>();\n for (const edge of data.relationships) {\n degree.set(edge.sourceEntityId, (degree.get(edge.sourceEntityId) ?? 0) + 1);\n degree.set(edge.targetEntityId, (degree.get(edge.targetEntityId) ?? 0) + 1);\n }\n\n const ranked = [...data.entities].sort((a, b) => {\n const pinnedDelta = Number(pinned.has(b.id)) - Number(pinned.has(a.id));\n if (pinnedDelta !== 0) return pinnedDelta;\n return (degree.get(b.id) ?? 0) - (degree.get(a.id) ?? 0) || a.id.localeCompare(b.id);\n });\n\n const keep = new Set(ranked.slice(0, maxNodes).map(e => e.id));\n return {\n entities: data.entities.filter(e => keep.has(e.id)),\n relationships: data.relationships.filter(\n r => keep.has(r.sourceEntityId) && keep.has(r.targetEntityId)\n ),\n };\n}\n","/**\n * Force simulation COPIED from the BUFI desk repo's bubble-memory view\n * (`packages/intelligence-ui/src/bubble-memory/bubble-physics.ts`,\n * plan 100, 2026-07-16). Copied — not imported — so this package publishes\n * standalone with zero `@bu/*` workspace dependencies. The bubble-memory\n * original stays untouched; a later dedup may flip intelligence-ui to\n * consume this copy.\n */\n\nexport interface PhysicsNode {\n id: string;\n x: number;\n y: number;\n vx: number;\n vy: number;\n radius: number;\n group: string; // entity type for clustering\n}\n\nexport interface PhysicsEdge {\n source: string;\n target: string;\n strength?: number; // 0–1, higher = closer together\n}\n\n// ── Tuning constants ─────────────────────────────────────────────────────────\n\nconst REPULSION_STRENGTH = 225; // charge repulsion — stronger for wider layout\nconst MAX_REPULSION_FORCE = 22; // cap force to prevent explosions\nconst SPRING_REST = 120; // wider rest length for constellation spacing\nconst SPRING_K = 0.055; // slightly softer springs\nconst COLLISION_PADDING = 18; // wider gap between node edges\nconst CENTER_GRAVITY = 0.025; // gentler center pull for spread layout\nconst DAMPING = 0.85; // velocity decay per step\nconst MAX_VELOCITY = 15; // cap velocity to prevent overshooting\nconst SIM_STEPS = 500; // steps for initial layout\nconst SEMANTIC_GRAVITY = 0.33; // same-type clustering pull\n\n// Search gravity — activated when user searches in the constellation\nexport const SEARCH_PULL = 0.25; // Strong pull toward origin for search hits\nexport const CLEARING_PUSH = 0.01; // Gentle push outward when clearing search\nexport const SEARCH_DAMPING = 0.88; // Slightly higher damping during search convergence\n\n// ── Initialization ───────────────────────────────────────────────────────────\n\nexport function initializePositions(nodes: PhysicsNode[], width: number, height: number): void {\n // Golden-angle spiral centered at (0,0)\n const goldenAngle = Math.PI * (3 - Math.sqrt(5));\n const maxRadius = Math.min(width, height) * 0.35;\n\n nodes.forEach((n, i) => {\n const angle = i * goldenAngle;\n const r = maxRadius * Math.sqrt((i + 1) / nodes.length);\n n.x = Math.cos(angle) * r;\n n.y = Math.sin(angle) * r;\n n.vx = 0;\n n.vy = 0;\n });\n}\n\n// ── Simulation step ──────────────────────────────────────────────────────────\n\nexport function simulateStep(\n nodes: PhysicsNode[],\n edges: PhysicsEdge[],\n _width: number,\n _height: number,\n activeSearchIds?: Set<string>\n): void {\n const nodeMap = new Map(nodes.map(n => [n.id, n]));\n\n // 1. Repulsion — Coulomb-like but capped to prevent explosions\n for (let i = 0; i < nodes.length; i++) {\n for (let j = i + 1; j < nodes.length; j++) {\n const a = nodes[i]!;\n const b = nodes[j]!;\n let dx = b.x - a.x;\n let dy = b.y - a.y;\n\n // Jitter to break zero-distance ties\n if (Math.abs(dx) < 0.5 && Math.abs(dy) < 0.5) {\n dx = (Math.random() - 0.5) * 4;\n dy = (Math.random() - 0.5) * 4;\n }\n\n const distSq = dx * dx + dy * dy;\n const dist = Math.sqrt(distSq);\n\n // Repulsion: force ∝ 1/dist (linear, not quadratic — more stable)\n const rawForce = REPULSION_STRENGTH / Math.max(dist, 1);\n const force = Math.min(rawForce, MAX_REPULSION_FORCE);\n\n const fx = (dx / dist) * force;\n const fy = (dy / dist) * force;\n a.vx -= fx;\n a.vy -= fy;\n b.vx += fx;\n b.vy += fy;\n }\n }\n\n // 2. Spring forces along edges (Hooke's law)\n for (const e of edges) {\n const a = nodeMap.get(e.source);\n const b = nodeMap.get(e.target);\n if (!a || !b) continue;\n\n const dx = b.x - a.x;\n const dy = b.y - a.y;\n const dist = Math.max(Math.sqrt(dx * dx + dy * dy), 1);\n\n const edgeStrength = e.strength ?? 0.5;\n // Stronger edges → shorter rest length (50–100px), weaker → longer (100–140px)\n const restLength = SPRING_REST * (1.4 - edgeStrength * 0.9);\n // Stronger edges → stiffer springs\n const k = SPRING_K * (0.5 + edgeStrength);\n\n const displacement = dist - restLength;\n const force = displacement * k;\n const fx = (dx / dist) * force;\n const fy = (dy / dist) * force;\n\n a.vx += fx;\n a.vy += fy;\n b.vx -= fx;\n b.vy -= fy;\n }\n\n // 3. Centre / search gravity\n const searching = activeSearchIds && activeSearchIds.size > 0;\n\n for (const n of nodes) {\n const dx = -n.x;\n const dy = -n.y;\n const dist = Math.sqrt(dx * dx + dy * dy) || 1;\n\n if (searching) {\n if (activeSearchIds.has(n.id)) {\n // Strong pull toward center for hits\n n.vx += (dx / dist) * SEARCH_PULL * dist * 0.01;\n n.vy += (dy / dist) * SEARCH_PULL * dist * 0.01;\n } else {\n // Gentle outward push for non-hits\n n.vx -= (dx / dist) * CLEARING_PUSH;\n n.vy -= (dy / dist) * CLEARING_PUSH;\n }\n } else {\n // Normal center gravity (existing behavior)\n n.vx += dx * CENTER_GRAVITY;\n n.vy += dy * CENTER_GRAVITY;\n }\n }\n\n // 3b. Semantic gravity — same-type nodes gently attract each other\n for (let i = 0; i < nodes.length; i++) {\n for (let j = i + 1; j < nodes.length; j++) {\n const a = nodes[i]!;\n const b = nodes[j]!;\n if (a.group !== b.group) continue;\n\n const dx = b.x - a.x;\n const dy = b.y - a.y;\n const dist = Math.sqrt(dx * dx + dy * dy);\n if (dist < 1 || dist > 300) continue; // Only cluster within range\n\n const force = SEMANTIC_GRAVITY * Math.min(dist * 0.1, 2);\n const fx = (dx / dist) * force;\n const fy = (dy / dist) * force;\n a.vx += fx;\n a.vy += fy;\n b.vx -= fx;\n b.vy -= fy;\n }\n }\n\n // 4. Apply velocity with damping and speed cap\n const currentDamping = searching ? SEARCH_DAMPING : DAMPING;\n for (const n of nodes) {\n n.vx *= currentDamping;\n n.vy *= currentDamping;\n\n // Cap velocity\n const speed = Math.sqrt(n.vx * n.vx + n.vy * n.vy);\n if (speed > MAX_VELOCITY) {\n n.vx = (n.vx / speed) * MAX_VELOCITY;\n n.vy = (n.vy / speed) * MAX_VELOCITY;\n }\n\n n.x += n.vx;\n n.y += n.vy;\n }\n\n // 5. Hard collision resolution — push overlapping nodes apart\n for (let i = 0; i < nodes.length; i++) {\n for (let j = i + 1; j < nodes.length; j++) {\n const a = nodes[i]!;\n const b = nodes[j]!;\n const dx = b.x - a.x;\n const dy = b.y - a.y;\n const dist = Math.sqrt(dx * dx + dy * dy);\n const minDist = a.radius + b.radius + COLLISION_PADDING;\n\n if (dist < minDist && dist > 0.01) {\n const overlap = (minDist - dist) / 2;\n const nx = dx / dist;\n const ny = dy / dist;\n a.x -= nx * overlap;\n a.y -= ny * overlap;\n b.x += nx * overlap;\n b.y += ny * overlap;\n // Also kill velocities along collision axis to prevent jitter\n const relVelDot = (b.vx - a.vx) * nx + (b.vy - a.vy) * ny;\n if (relVelDot < 0) {\n a.vx += nx * relVelDot * 0.5;\n a.vy += ny * relVelDot * 0.5;\n b.vx -= nx * relVelDot * 0.5;\n b.vy -= ny * relVelDot * 0.5;\n }\n }\n }\n }\n}\n\n// ── Full simulation (synchronous) ────────────────────────────────────────────\n\nexport function runSimulation(\n nodes: PhysicsNode[],\n edges: PhysicsEdge[],\n width: number,\n height: number\n): void {\n initializePositions(nodes, width, height);\n for (let i = 0; i < SIM_STEPS; i++) {\n simulateStep(nodes, edges, width, height);\n }\n}\n\n// ── Search amplitude scaling ─────────────────────────────────────────────────\n\nexport function getFloatingAmplitudeScale(nodeId: string, activeSearchIds?: Set<string>): number {\n if (!activeSearchIds || activeSearchIds.size === 0) return 1.0;\n return activeSearchIds.has(nodeId) ? 0.15 : 0.6;\n}\n\n// ── Deterministic floating motion ────────────────────────────────────────────\n\nexport function getMotionSeed(id: string): { phase: number; amplitude: number; speed: number } {\n let hash = 0;\n for (let i = 0; i < id.length; i++) {\n hash = ((hash << 5) - hash + id.charCodeAt(i)) | 0;\n }\n const norm = (v: number) => (((v % 1000) + 1000) % 1000) / 1000;\n return {\n phase: norm(hash) * Math.PI * 2,\n amplitude: 2 + norm(hash >> 8) * 2.5,\n speed: 0.0003 + norm(hash >> 16) * 0.0003,\n };\n}\n\nexport function getFloatingOffset(id: string, time: number): { dx: number; dy: number } {\n const seed = getMotionSeed(id);\n const phase = seed.phase + time * seed.speed;\n return {\n dx: Math.sin(phase) * seed.amplitude,\n dy: Math.cos(phase * 0.9) * seed.amplitude,\n };\n}\n","/**\n * Visual constants for `GtmGraphView`. Entity palette adapted from the desk\n * repo's bubble-memory `ENTITY_COLORS`; class accents follow the BUFI\n * DESIGN.md brand palette (purpleDanis #6954cf, main #647ce6, agnusDeiDanis\n * #ffebb4, belanovaDanis #ffadec). All styling here is inline-SVG/inline-CSS\n * so the component renders identically in any host regardless of its\n * Tailwind config (custom desk tokens don't exist in open-agents).\n */\n\nexport interface EntityColor {\n fill: string;\n glow: string;\n stroke: string;\n label: string;\n}\n\nexport const ENTITY_COLORS: Record<string, EntityColor> = {\n person: { fill: '#D4BFFF', glow: '#B794FF', stroke: '#A78BFA', label: 'People' },\n organization: { fill: '#818CF8', glow: '#6366F1', stroke: '#4F46E5', label: 'Organizations' },\n project: { fill: '#86EFAC', glow: '#4ADE80', stroke: '#22C55E', label: 'Deals' },\n meeting: { fill: '#FDE68A', glow: '#FBBF24', stroke: '#F59E0B', label: 'Meetings' },\n topic: { fill: '#F9A8D4', glow: '#F472B6', stroke: '#EC4899', label: 'Topics' },\n contact: { fill: '#99F6E4', glow: '#5EEAD4', stroke: '#2DD4BF', label: 'Contacts' },\n};\n\nexport const FALLBACK_ENTITY_COLOR: EntityColor = {\n fill: '#9CA3AF',\n glow: '#9CA3AF',\n stroke: '#6B7280',\n label: 'Other',\n};\n\nexport function entityColor(entityType: string): EntityColor {\n return ENTITY_COLORS[entityType] ?? FALLBACK_ENTITY_COLOR;\n}\n\n/** Node-class accents (ring around the node). */\nexport const CLASS_ACCENTS = {\n /** POI — BUFI brand purple (purpleDanis). */\n poi: '#6954CF',\n /** Bridge — warm attention amber (agnusDeiDanis family, darkened for contrast). */\n bridge: '#D9A514',\n} as const;\n\n/** Path-trace accents. */\nexport const TRACE_ACCENTS = {\n strongest: '#6954CF',\n fewest: '#647CE6',\n endpoints: '#FFADEC',\n} as const;\n\nexport interface EdgeTypeStyle {\n /** Dash pattern; undefined = solid. */\n dash?: string;\n label: string;\n}\n\n/**\n * Per-relationship-type styling. Includes `li_connection` up front (spec:\n * HeyReach LinkedIn network edges are pre-wired so the UI needs zero changes\n * when the 099 follow-up lands).\n */\nexport const EDGE_TYPE_STYLES: Record<string, EdgeTypeStyle> = {\n works_at: { label: 'works at' },\n related_to: { dash: '4 3', label: 'related to' },\n champion_of: { label: 'champion of' },\n decision_maker_for: { label: 'decision maker' },\n owner_of_deal: { label: 'deal owner' },\n called: { dash: '2 3', label: 'called' },\n messaged: { dash: '2 3', label: 'messaged' },\n emailed: { dash: '2 3', label: 'emailed' },\n replied_to: { dash: '2 3', label: 'replied' },\n met_with: { dash: '6 3', label: 'met with' },\n supports: { dash: '4 3', label: 'support ticket' },\n enrolled_in: { dash: '1 3', label: 'in campaign' },\n intro_path: { dash: '8 4', label: 'warm intro' },\n li_connection: { dash: '1 2', label: 'LinkedIn connection' },\n};\n\nexport function edgeTypeStyle(relationshipType: string): EdgeTypeStyle {\n return (\n EDGE_TYPE_STYLES[relationshipType] ?? {\n dash: '4 3',\n label: relationshipType.replace(/_/g, ' '),\n }\n );\n}\n\nexport function nodeRadius(entityType: string): number {\n switch (entityType) {\n case 'organization':\n return 16;\n case 'project':\n return 13;\n case 'person':\n case 'contact':\n return 12;\n default:\n return 10;\n }\n}\n","/**\n * Pure view-model builders for `GtmGraphView` — kept free of React so the\n * class assignment, recency fading, and trace-highlight logic is unit-testable\n * without a DOM.\n */\n\nimport {\n classifyNode,\n computeBridgeIds,\n computePoiIds,\n fewestHops,\n type GraphPath,\n type GtmNodeClass,\n recencyFactor,\n strongestPath,\n} from '../algorithms';\nimport type { GtmGraphData, GtmGraphDecorations } from '../types';\nimport { CLASS_ACCENTS, edgeTypeStyle, entityColor, nodeRadius } from './theme';\n\nexport interface NodeVisual {\n id: string;\n entityName: string;\n entityType: string;\n nodeClass: GtmNodeClass;\n radius: number;\n fill: string;\n glow: string;\n stroke: string;\n /** Class ring color; null for `other` nodes. */\n ring: string | null;\n /** True when the node matches the active search query (or no search). */\n matched: boolean;\n}\n\nexport interface EdgeVisual {\n id: string;\n sourceEntityId: string;\n targetEntityId: string;\n relationshipType: string;\n label: string;\n dash?: string;\n /** 0–1 physics spring strength. */\n springStrength: number;\n /** Base opacity — strength × recency fading (stale edges fade out). */\n opacity: number;\n evidenceCount: number;\n}\n\nexport interface TraceResult {\n fromId: string;\n toId: string;\n strongest: GraphPath | null;\n fewest: GraphPath | null;\n /** True when both paths exist and traverse different node chains. */\n diverges: boolean;\n}\n\nconst MIN_EDGE_OPACITY = 0.08;\nconst MAX_EDGE_OPACITY = 0.8;\n\n/** Resolve node classes from server decorations, computing bridges locally when absent. */\nexport function resolveClasses(\n data: GtmGraphData,\n decorations: Partial<GtmGraphDecorations> | undefined,\n options: { bridgeTopN?: number; now?: number } = {}\n): { poiIds: Set<string>; bridgeIds: Set<string> } {\n const poiIds = decorations?.poiIds ? new Set(decorations.poiIds) : computePoiIds(data);\n let bridgeIds: Set<string>;\n if (decorations?.bridgeScores) {\n const topN = options.bridgeTopN ?? 5;\n bridgeIds = new Set(\n Object.entries(decorations.bridgeScores)\n .filter(([, score]) => score > 0)\n .sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0]))\n .slice(0, topN)\n .map(([id]) => id)\n );\n } else {\n bridgeIds = computeBridgeIds(data, { topN: options.bridgeTopN ?? 5, now: options.now ?? 0 });\n }\n return { poiIds, bridgeIds };\n}\n\nexport function buildNodeVisuals(\n data: GtmGraphData,\n poiIds: ReadonlySet<string>,\n bridgeIds: ReadonlySet<string>,\n searchQuery: string\n): NodeVisual[] {\n const query = searchQuery.trim().toLowerCase();\n return data.entities.map(entity => {\n const nodeClass = classifyNode(entity.id, poiIds, bridgeIds);\n const colors = entityColor(entity.entityType);\n return {\n id: entity.id,\n entityName: entity.entityName,\n entityType: entity.entityType,\n nodeClass,\n radius: nodeRadius(entity.entityType),\n fill: colors.fill,\n glow: colors.glow,\n stroke: colors.stroke,\n ring:\n nodeClass === 'poi'\n ? CLASS_ACCENTS.poi\n : nodeClass === 'bridge'\n ? CLASS_ACCENTS.bridge\n : null,\n matched: query.length === 0 || entity.entityName.toLowerCase().includes(query),\n };\n });\n}\n\nexport function buildEdgeVisuals(data: GtmGraphData, now: number): EdgeVisual[] {\n return data.relationships.map(edge => {\n const strength = Math.min(1, Math.max(0, edge.strength ?? 0.5));\n const recency = recencyFactor(edge.lastSeenAt, now);\n const style = edgeTypeStyle(edge.relationshipType);\n const opacity = Math.min(\n MAX_EDGE_OPACITY,\n Math.max(MIN_EDGE_OPACITY, (0.15 + strength * 0.55) * recency)\n );\n return {\n id: edge.id,\n sourceEntityId: edge.sourceEntityId,\n targetEntityId: edge.targetEntityId,\n relationshipType: edge.relationshipType,\n label: style.label,\n dash: style.dash,\n springStrength: strength,\n opacity,\n evidenceCount: edge.evidenceCount ?? 1,\n };\n });\n}\n\n/** Strongest + fewest paths between a picked pair — the warm-path tracer. */\nexport function buildTrace(\n data: GtmGraphData,\n fromId: string,\n toId: string,\n now: number\n): TraceResult {\n const strongest = strongestPath(data, fromId, toId, { now });\n const fewest = fewestHops(data, fromId, toId, { now });\n const diverges =\n strongest !== null &&\n fewest !== null &&\n strongest.nodeIds.join('|') !== fewest.nodeIds.join('|');\n return { fromId, toId, strongest, fewest, diverges };\n}\n\nexport function traceEdgeIds(trace: TraceResult | null): {\n strongest: Set<string>;\n fewest: Set<string>;\n nodes: Set<string>;\n} {\n const strongest = new Set(trace?.strongest?.edgeIds ?? []);\n const fewest = new Set(trace?.fewest?.edgeIds ?? []);\n const nodes = new Set([...(trace?.strongest?.nodeIds ?? []), ...(trace?.fewest?.nodeIds ?? [])]);\n return { strongest, fewest, nodes };\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,183 @@
1
+ import * as react from 'react';
2
+
3
+ /**
4
+ * Graph data contracts for `@bufinance/gtm-graph`.
5
+ *
6
+ * Deliberately a MINIMAL structural duplicate of the BUFI desk repo's
7
+ * `KnowledgeGraphData` (`@bu/types/intelligence`) — this package is published
8
+ * standalone and cannot import `@bu/*`. Structural compatibility is asserted
9
+ * by a compat test inside the desk repo
10
+ * (`packages/intelligence/src/knowledge/gtm-graph-compat.test.ts`).
11
+ *
12
+ * `lastSeenAt`/`metadata` on edges are optional because the desk's
13
+ * `get_knowledge_graph` RPC omits them; hosts that fetch
14
+ * `entity_relationships` directly should include both so recency fading and
15
+ * true traversal weights work.
16
+ */
17
+ interface GtmGraphEntity {
18
+ id: string;
19
+ entityType: string;
20
+ entityName: string;
21
+ content?: string | null;
22
+ metadata?: Record<string, unknown> | null;
23
+ createdAt?: string | null;
24
+ updatedAt?: string | null;
25
+ }
26
+ interface GtmGraphEdge {
27
+ id: string;
28
+ sourceEntityId: string;
29
+ targetEntityId: string;
30
+ relationshipType: string;
31
+ strength?: number | null;
32
+ evidenceCount?: number | null;
33
+ lastSeenAt?: string | null;
34
+ metadata?: Record<string, unknown> | null;
35
+ }
36
+ interface GtmGraphData {
37
+ entities: GtmGraphEntity[];
38
+ relationships: GtmGraphEdge[];
39
+ }
40
+ /** Server-computed decorations shipped alongside the graph payload. */
41
+ interface GtmGraphDecorations {
42
+ /** Person-of-interest entity ids (spec: champions/decision-makers on open deals + ICP title matches). */
43
+ poiIds: string[];
44
+ /** Normalized [0,1] betweenness per entity id (only nonzero entries need be present). */
45
+ bridgeScores: Record<string, number>;
46
+ }
47
+
48
+ interface GtmGraphViewProps {
49
+ data: GtmGraphData;
50
+ /** Server-computed decorations; missing pieces are computed client-side. */
51
+ decorations?: Partial<GtmGraphDecorations>;
52
+ /** Epoch ms driving recency fading + path weights (default: Date.now() at mount). */
53
+ now?: number;
54
+ /** Render at most this many nodes (highest-degree first); an Expand control shows the rest. Default 400. */
55
+ maxNodes?: number;
56
+ /** How many top-betweenness nodes get the bridge accent. Default 5. */
57
+ bridgeTopN?: number;
58
+ /** Node click callback — hosts own the detail UI. */
59
+ onEntitySelect?: (entityId: string) => void;
60
+ /** Viewport height in px (width is fluid). Default 560. */
61
+ height?: number;
62
+ /** Dark theme toggle — hosts pass their theme state. Default false. */
63
+ dark?: boolean;
64
+ }
65
+ declare function GtmGraphView({ data, decorations, now, maxNodes, bridgeTopN, onEntitySelect, height, dark, }: GtmGraphViewProps): react.JSX.Element;
66
+
67
+ /**
68
+ * Visual constants for `GtmGraphView`. Entity palette adapted from the desk
69
+ * repo's bubble-memory `ENTITY_COLORS`; class accents follow the BUFI
70
+ * DESIGN.md brand palette (purpleDanis #6954cf, main #647ce6, agnusDeiDanis
71
+ * #ffebb4, belanovaDanis #ffadec). All styling here is inline-SVG/inline-CSS
72
+ * so the component renders identically in any host regardless of its
73
+ * Tailwind config (custom desk tokens don't exist in open-agents).
74
+ */
75
+ interface EntityColor {
76
+ fill: string;
77
+ glow: string;
78
+ stroke: string;
79
+ label: string;
80
+ }
81
+ declare const ENTITY_COLORS: Record<string, EntityColor>;
82
+ declare const FALLBACK_ENTITY_COLOR: EntityColor;
83
+ declare function entityColor(entityType: string): EntityColor;
84
+ /** Node-class accents (ring around the node). */
85
+ declare const CLASS_ACCENTS: {
86
+ /** POI — BUFI brand purple (purpleDanis). */
87
+ readonly poi: "#6954CF";
88
+ /** Bridge — warm attention amber (agnusDeiDanis family, darkened for contrast). */
89
+ readonly bridge: "#D9A514";
90
+ };
91
+ /** Path-trace accents. */
92
+ declare const TRACE_ACCENTS: {
93
+ readonly strongest: "#6954CF";
94
+ readonly fewest: "#647CE6";
95
+ readonly endpoints: "#FFADEC";
96
+ };
97
+ interface EdgeTypeStyle {
98
+ /** Dash pattern; undefined = solid. */
99
+ dash?: string;
100
+ label: string;
101
+ }
102
+ /**
103
+ * Per-relationship-type styling. Includes `li_connection` up front (spec:
104
+ * HeyReach LinkedIn network edges are pre-wired so the UI needs zero changes
105
+ * when the 099 follow-up lands).
106
+ */
107
+ declare const EDGE_TYPE_STYLES: Record<string, EdgeTypeStyle>;
108
+ declare function edgeTypeStyle(relationshipType: string): EdgeTypeStyle;
109
+ declare function nodeRadius(entityType: string): number;
110
+
111
+ interface GraphPath {
112
+ /** Node ids from source to target (inclusive). */
113
+ nodeIds: string[];
114
+ /** Edge ids traversed, in order (`nodeIds.length - 1` entries). */
115
+ edgeIds: string[];
116
+ /** Hop count (= edgeIds.length). */
117
+ hops: number;
118
+ /** Sum of Dijkstra costs (1/w) — lower = stronger; 0 for the trivial path. */
119
+ cost: number;
120
+ }
121
+
122
+ type GtmNodeClass = 'poi' | 'bridge' | 'other';
123
+
124
+ /**
125
+ * Pure view-model builders for `GtmGraphView` — kept free of React so the
126
+ * class assignment, recency fading, and trace-highlight logic is unit-testable
127
+ * without a DOM.
128
+ */
129
+
130
+ interface NodeVisual {
131
+ id: string;
132
+ entityName: string;
133
+ entityType: string;
134
+ nodeClass: GtmNodeClass;
135
+ radius: number;
136
+ fill: string;
137
+ glow: string;
138
+ stroke: string;
139
+ /** Class ring color; null for `other` nodes. */
140
+ ring: string | null;
141
+ /** True when the node matches the active search query (or no search). */
142
+ matched: boolean;
143
+ }
144
+ interface EdgeVisual {
145
+ id: string;
146
+ sourceEntityId: string;
147
+ targetEntityId: string;
148
+ relationshipType: string;
149
+ label: string;
150
+ dash?: string;
151
+ /** 0–1 physics spring strength. */
152
+ springStrength: number;
153
+ /** Base opacity — strength × recency fading (stale edges fade out). */
154
+ opacity: number;
155
+ evidenceCount: number;
156
+ }
157
+ interface TraceResult {
158
+ fromId: string;
159
+ toId: string;
160
+ strongest: GraphPath | null;
161
+ fewest: GraphPath | null;
162
+ /** True when both paths exist and traverse different node chains. */
163
+ diverges: boolean;
164
+ }
165
+ /** Resolve node classes from server decorations, computing bridges locally when absent. */
166
+ declare function resolveClasses(data: GtmGraphData, decorations: Partial<GtmGraphDecorations> | undefined, options?: {
167
+ bridgeTopN?: number;
168
+ now?: number;
169
+ }): {
170
+ poiIds: Set<string>;
171
+ bridgeIds: Set<string>;
172
+ };
173
+ declare function buildNodeVisuals(data: GtmGraphData, poiIds: ReadonlySet<string>, bridgeIds: ReadonlySet<string>, searchQuery: string): NodeVisual[];
174
+ declare function buildEdgeVisuals(data: GtmGraphData, now: number): EdgeVisual[];
175
+ /** Strongest + fewest paths between a picked pair — the warm-path tracer. */
176
+ declare function buildTrace(data: GtmGraphData, fromId: string, toId: string, now: number): TraceResult;
177
+ declare function traceEdgeIds(trace: TraceResult | null): {
178
+ strongest: Set<string>;
179
+ fewest: Set<string>;
180
+ nodes: Set<string>;
181
+ };
182
+
183
+ export { CLASS_ACCENTS, EDGE_TYPE_STYLES, ENTITY_COLORS, type EdgeTypeStyle, type EdgeVisual, type EntityColor, FALLBACK_ENTITY_COLOR, GtmGraphView, type GtmGraphViewProps, type NodeVisual, TRACE_ACCENTS, type TraceResult, buildEdgeVisuals, buildNodeVisuals, buildTrace, edgeTypeStyle, entityColor, nodeRadius, resolveClasses, traceEdgeIds };
@@ -0,0 +1,183 @@
1
+ import * as react from 'react';
2
+
3
+ /**
4
+ * Graph data contracts for `@bufinance/gtm-graph`.
5
+ *
6
+ * Deliberately a MINIMAL structural duplicate of the BUFI desk repo's
7
+ * `KnowledgeGraphData` (`@bu/types/intelligence`) — this package is published
8
+ * standalone and cannot import `@bu/*`. Structural compatibility is asserted
9
+ * by a compat test inside the desk repo
10
+ * (`packages/intelligence/src/knowledge/gtm-graph-compat.test.ts`).
11
+ *
12
+ * `lastSeenAt`/`metadata` on edges are optional because the desk's
13
+ * `get_knowledge_graph` RPC omits them; hosts that fetch
14
+ * `entity_relationships` directly should include both so recency fading and
15
+ * true traversal weights work.
16
+ */
17
+ interface GtmGraphEntity {
18
+ id: string;
19
+ entityType: string;
20
+ entityName: string;
21
+ content?: string | null;
22
+ metadata?: Record<string, unknown> | null;
23
+ createdAt?: string | null;
24
+ updatedAt?: string | null;
25
+ }
26
+ interface GtmGraphEdge {
27
+ id: string;
28
+ sourceEntityId: string;
29
+ targetEntityId: string;
30
+ relationshipType: string;
31
+ strength?: number | null;
32
+ evidenceCount?: number | null;
33
+ lastSeenAt?: string | null;
34
+ metadata?: Record<string, unknown> | null;
35
+ }
36
+ interface GtmGraphData {
37
+ entities: GtmGraphEntity[];
38
+ relationships: GtmGraphEdge[];
39
+ }
40
+ /** Server-computed decorations shipped alongside the graph payload. */
41
+ interface GtmGraphDecorations {
42
+ /** Person-of-interest entity ids (spec: champions/decision-makers on open deals + ICP title matches). */
43
+ poiIds: string[];
44
+ /** Normalized [0,1] betweenness per entity id (only nonzero entries need be present). */
45
+ bridgeScores: Record<string, number>;
46
+ }
47
+
48
+ interface GtmGraphViewProps {
49
+ data: GtmGraphData;
50
+ /** Server-computed decorations; missing pieces are computed client-side. */
51
+ decorations?: Partial<GtmGraphDecorations>;
52
+ /** Epoch ms driving recency fading + path weights (default: Date.now() at mount). */
53
+ now?: number;
54
+ /** Render at most this many nodes (highest-degree first); an Expand control shows the rest. Default 400. */
55
+ maxNodes?: number;
56
+ /** How many top-betweenness nodes get the bridge accent. Default 5. */
57
+ bridgeTopN?: number;
58
+ /** Node click callback — hosts own the detail UI. */
59
+ onEntitySelect?: (entityId: string) => void;
60
+ /** Viewport height in px (width is fluid). Default 560. */
61
+ height?: number;
62
+ /** Dark theme toggle — hosts pass their theme state. Default false. */
63
+ dark?: boolean;
64
+ }
65
+ declare function GtmGraphView({ data, decorations, now, maxNodes, bridgeTopN, onEntitySelect, height, dark, }: GtmGraphViewProps): react.JSX.Element;
66
+
67
+ /**
68
+ * Visual constants for `GtmGraphView`. Entity palette adapted from the desk
69
+ * repo's bubble-memory `ENTITY_COLORS`; class accents follow the BUFI
70
+ * DESIGN.md brand palette (purpleDanis #6954cf, main #647ce6, agnusDeiDanis
71
+ * #ffebb4, belanovaDanis #ffadec). All styling here is inline-SVG/inline-CSS
72
+ * so the component renders identically in any host regardless of its
73
+ * Tailwind config (custom desk tokens don't exist in open-agents).
74
+ */
75
+ interface EntityColor {
76
+ fill: string;
77
+ glow: string;
78
+ stroke: string;
79
+ label: string;
80
+ }
81
+ declare const ENTITY_COLORS: Record<string, EntityColor>;
82
+ declare const FALLBACK_ENTITY_COLOR: EntityColor;
83
+ declare function entityColor(entityType: string): EntityColor;
84
+ /** Node-class accents (ring around the node). */
85
+ declare const CLASS_ACCENTS: {
86
+ /** POI — BUFI brand purple (purpleDanis). */
87
+ readonly poi: "#6954CF";
88
+ /** Bridge — warm attention amber (agnusDeiDanis family, darkened for contrast). */
89
+ readonly bridge: "#D9A514";
90
+ };
91
+ /** Path-trace accents. */
92
+ declare const TRACE_ACCENTS: {
93
+ readonly strongest: "#6954CF";
94
+ readonly fewest: "#647CE6";
95
+ readonly endpoints: "#FFADEC";
96
+ };
97
+ interface EdgeTypeStyle {
98
+ /** Dash pattern; undefined = solid. */
99
+ dash?: string;
100
+ label: string;
101
+ }
102
+ /**
103
+ * Per-relationship-type styling. Includes `li_connection` up front (spec:
104
+ * HeyReach LinkedIn network edges are pre-wired so the UI needs zero changes
105
+ * when the 099 follow-up lands).
106
+ */
107
+ declare const EDGE_TYPE_STYLES: Record<string, EdgeTypeStyle>;
108
+ declare function edgeTypeStyle(relationshipType: string): EdgeTypeStyle;
109
+ declare function nodeRadius(entityType: string): number;
110
+
111
+ interface GraphPath {
112
+ /** Node ids from source to target (inclusive). */
113
+ nodeIds: string[];
114
+ /** Edge ids traversed, in order (`nodeIds.length - 1` entries). */
115
+ edgeIds: string[];
116
+ /** Hop count (= edgeIds.length). */
117
+ hops: number;
118
+ /** Sum of Dijkstra costs (1/w) — lower = stronger; 0 for the trivial path. */
119
+ cost: number;
120
+ }
121
+
122
+ type GtmNodeClass = 'poi' | 'bridge' | 'other';
123
+
124
+ /**
125
+ * Pure view-model builders for `GtmGraphView` — kept free of React so the
126
+ * class assignment, recency fading, and trace-highlight logic is unit-testable
127
+ * without a DOM.
128
+ */
129
+
130
+ interface NodeVisual {
131
+ id: string;
132
+ entityName: string;
133
+ entityType: string;
134
+ nodeClass: GtmNodeClass;
135
+ radius: number;
136
+ fill: string;
137
+ glow: string;
138
+ stroke: string;
139
+ /** Class ring color; null for `other` nodes. */
140
+ ring: string | null;
141
+ /** True when the node matches the active search query (or no search). */
142
+ matched: boolean;
143
+ }
144
+ interface EdgeVisual {
145
+ id: string;
146
+ sourceEntityId: string;
147
+ targetEntityId: string;
148
+ relationshipType: string;
149
+ label: string;
150
+ dash?: string;
151
+ /** 0–1 physics spring strength. */
152
+ springStrength: number;
153
+ /** Base opacity — strength × recency fading (stale edges fade out). */
154
+ opacity: number;
155
+ evidenceCount: number;
156
+ }
157
+ interface TraceResult {
158
+ fromId: string;
159
+ toId: string;
160
+ strongest: GraphPath | null;
161
+ fewest: GraphPath | null;
162
+ /** True when both paths exist and traverse different node chains. */
163
+ diverges: boolean;
164
+ }
165
+ /** Resolve node classes from server decorations, computing bridges locally when absent. */
166
+ declare function resolveClasses(data: GtmGraphData, decorations: Partial<GtmGraphDecorations> | undefined, options?: {
167
+ bridgeTopN?: number;
168
+ now?: number;
169
+ }): {
170
+ poiIds: Set<string>;
171
+ bridgeIds: Set<string>;
172
+ };
173
+ declare function buildNodeVisuals(data: GtmGraphData, poiIds: ReadonlySet<string>, bridgeIds: ReadonlySet<string>, searchQuery: string): NodeVisual[];
174
+ declare function buildEdgeVisuals(data: GtmGraphData, now: number): EdgeVisual[];
175
+ /** Strongest + fewest paths between a picked pair — the warm-path tracer. */
176
+ declare function buildTrace(data: GtmGraphData, fromId: string, toId: string, now: number): TraceResult;
177
+ declare function traceEdgeIds(trace: TraceResult | null): {
178
+ strongest: Set<string>;
179
+ fewest: Set<string>;
180
+ nodes: Set<string>;
181
+ };
182
+
183
+ export { CLASS_ACCENTS, EDGE_TYPE_STYLES, ENTITY_COLORS, type EdgeTypeStyle, type EdgeVisual, type EntityColor, FALLBACK_ENTITY_COLOR, GtmGraphView, type GtmGraphViewProps, type NodeVisual, TRACE_ACCENTS, type TraceResult, buildEdgeVisuals, buildNodeVisuals, buildTrace, edgeTypeStyle, entityColor, nodeRadius, resolveClasses, traceEdgeIds };