@copilotz/admin 0.9.40 → 0.9.42

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1956,20 +1956,165 @@ function AgentDetailPage({ context }) {
1956
1956
  // src/modules/brain/index.tsx
1957
1957
  import React7 from "react";
1958
1958
  import {
1959
+ BookOpen,
1959
1960
  Brain as Brain2,
1960
- CircleDot,
1961
1961
  GitBranch,
1962
- Network,
1963
1962
  RefreshCw,
1964
1963
  Search as Search2,
1965
- Sparkles
1964
+ Sparkles,
1965
+ UsersRound
1966
1966
  } from "lucide-react";
1967
+
1968
+ // src/modules/brain/view-model.ts
1969
+ var ENTITY_FOCUS_RELATION_TYPES = [
1970
+ "mentions",
1971
+ "related_to",
1972
+ "supports",
1973
+ "depends_on",
1974
+ "contradicts",
1975
+ "supersedes"
1976
+ ];
1977
+ var BRAIN_VIEW_LABELS = {
1978
+ entities: "Entities",
1979
+ knowledge: "Knowledge",
1980
+ work: "Work",
1981
+ map: "Map",
1982
+ all: "All nodes"
1983
+ };
1984
+ var BRAIN_RELATION_GROUP_DEFINITIONS = [
1985
+ {
1986
+ id: "decisions",
1987
+ label: "Decisions",
1988
+ description: "Durable choices connected to this entity.",
1989
+ kinds: ["decision"]
1990
+ },
1991
+ {
1992
+ id: "facts",
1993
+ label: "Facts",
1994
+ description: "Known statements and observations.",
1995
+ kinds: ["fact"]
1996
+ },
1997
+ {
1998
+ id: "tasks",
1999
+ label: "Tasks",
2000
+ description: "Actions, next steps, and execution work.",
2001
+ kinds: ["task", "next_action"]
2002
+ },
2003
+ {
2004
+ id: "preferences",
2005
+ label: "Preferences",
2006
+ description: "Remembered user, org, or agent preferences.",
2007
+ kinds: ["preference"]
2008
+ },
2009
+ {
2010
+ id: "constraints",
2011
+ label: "Constraints",
2012
+ description: "Limits, rules, and requirements.",
2013
+ kinds: ["constraint"]
2014
+ },
2015
+ {
2016
+ id: "risks",
2017
+ label: "Risks",
2018
+ description: "Known risks or possible failures.",
2019
+ kinds: ["risk"]
2020
+ },
2021
+ {
2022
+ id: "openQuestions",
2023
+ label: "Open questions",
2024
+ description: "Questions that still need resolution.",
2025
+ kinds: ["open_question"]
2026
+ },
2027
+ {
2028
+ id: "currentState",
2029
+ label: "Current state",
2030
+ description: "Shorter-term context, challenges, and active state.",
2031
+ kinds: ["current_state", "challenge"]
2032
+ },
2033
+ {
2034
+ id: "entities",
2035
+ label: "Other entities",
2036
+ description: "Neighboring people, systems, projects, or concepts.",
2037
+ kinds: ["entity"]
2038
+ },
2039
+ {
2040
+ id: "other",
2041
+ label: "Other",
2042
+ description: "Related nodes that do not fit a standard Brain kind yet.",
2043
+ kinds: []
2044
+ }
2045
+ ];
2046
+ var GROUP_BY_KIND = new Map(
2047
+ BRAIN_RELATION_GROUP_DEFINITIONS.flatMap(
2048
+ (group) => group.kinds.map((kind) => [kind, group.id])
2049
+ )
2050
+ );
2051
+ function getBrainViewBaseFilters(view) {
2052
+ if (view === "entities") {
2053
+ return { kind: "entity", layer: "knowledge" };
2054
+ }
2055
+ if (view === "knowledge") {
2056
+ return { kind: "all", layer: "knowledge" };
2057
+ }
2058
+ if (view === "work") {
2059
+ return { kind: "all", layer: "working" };
2060
+ }
2061
+ return { kind: "all", layer: "all" };
2062
+ }
2063
+ function isEntityBrainView(view) {
2064
+ return view === "entities";
2065
+ }
2066
+ function groupBrainRelationsByKind(related) {
2067
+ const groups = BRAIN_RELATION_GROUP_DEFINITIONS.map((definition) => ({
2068
+ id: definition.id,
2069
+ label: definition.label,
2070
+ description: definition.description,
2071
+ items: []
2072
+ }));
2073
+ const byId = new Map(groups.map((group) => [group.id, group]));
2074
+ for (const item of related) {
2075
+ const groupId = GROUP_BY_KIND.get(item.node.kind) ?? "other";
2076
+ byId.get(groupId)?.items.push(item);
2077
+ }
2078
+ return groups.filter((group) => group.items.length > 0);
2079
+ }
2080
+ function getKnowledgeRelationGroups(related) {
2081
+ const knowledgeGroupIds = [
2082
+ "decisions",
2083
+ "facts",
2084
+ "preferences",
2085
+ "constraints"
2086
+ ];
2087
+ return groupBrainRelationsByKind(related).filter(
2088
+ (group) => knowledgeGroupIds.includes(group.id)
2089
+ );
2090
+ }
2091
+ function getWorkRelationGroups(related) {
2092
+ const workGroupIds = [
2093
+ "tasks",
2094
+ "risks",
2095
+ "openQuestions",
2096
+ "currentState"
2097
+ ];
2098
+ return groupBrainRelationsByKind(related).filter(
2099
+ (group) => workGroupIds.includes(group.id)
2100
+ );
2101
+ }
2102
+
2103
+ // src/modules/brain/index.tsx
1967
2104
  import { jsx as jsx21, jsxs as jsxs13 } from "react/jsx-runtime";
1968
2105
  var BRAIN_LAYERS = ["all", "knowledge", "working"];
1969
2106
  var BRAIN_SEARCH_MODES = ["hybrid", "semantic", "keyword"];
1970
2107
  var BRAIN_STATUSES = ["active", "all", "superseded", "archived"];
2108
+ var BRAIN_VIEWS = [
2109
+ "entities",
2110
+ "knowledge",
2111
+ "work",
2112
+ "map",
2113
+ "all"
2114
+ ];
1971
2115
  var BRAIN_KINDS = [
1972
2116
  "all",
2117
+ "entity",
1973
2118
  "decision",
1974
2119
  "fact",
1975
2120
  "preference",
@@ -2003,6 +2148,7 @@ function brainModule() {
2003
2148
  };
2004
2149
  }
2005
2150
  function BrainPage({ context }) {
2151
+ const [view, setView] = React7.useState("entities");
2006
2152
  const [search, setSearch] = React7.useState("");
2007
2153
  const [searchMode, setSearchMode] = React7.useState(
2008
2154
  "hybrid"
@@ -2026,26 +2172,37 @@ function BrainPage({ context }) {
2026
2172
  setLoading(true);
2027
2173
  setError(null);
2028
2174
  const effectiveFocusNodeId = focusOverride === void 0 ? focusNodeId : focusOverride;
2175
+ const viewFilters = getBrainViewBaseFilters(view);
2176
+ const effectiveLayer = view === "map" || view === "all" ? layer : viewFilters.layer;
2177
+ const effectiveKind = view === "entities" ? "entity" : kind;
2029
2178
  try {
2030
2179
  const next = await context.client.getBrain({
2031
2180
  namespace: context.scope.namespace || void 0,
2032
2181
  search: search.trim() || void 0,
2033
2182
  searchMode,
2034
- layer,
2183
+ layer: effectiveLayer,
2035
2184
  status,
2036
- kind,
2185
+ kind: effectiveKind,
2037
2186
  agentId: agentId.trim() || void 0,
2038
2187
  focusNodeId: effectiveFocusNodeId || void 0,
2039
2188
  includeRelated: Boolean(effectiveFocusNodeId),
2040
2189
  includeSimilar: Boolean(effectiveFocusNodeId),
2041
2190
  similarLimit: 24,
2042
2191
  minSimilarity: 0.2,
2192
+ relationTypes: effectiveFocusNodeId ? [...ENTITY_FOCUS_RELATION_TYPES] : void 0,
2043
2193
  limit: 180
2044
2194
  });
2045
2195
  setResponse(next);
2046
- setSelectedNode(
2047
- (current) => effectiveFocusNodeId ? next.nodes.find((node) => node.id === effectiveFocusNodeId) ?? current : current && next.nodes.some((node) => node.id === current.id) ? next.nodes.find((node) => node.id === current.id) ?? current : next.nodes[0] ?? null
2048
- );
2196
+ setSelectedNode((current) => {
2197
+ const currentInResults = current ? next.nodes.find((node) => node.id === current.id) ?? current : null;
2198
+ if (effectiveFocusNodeId) {
2199
+ return next.nodes.find((node) => node.id === effectiveFocusNodeId) ?? currentInResults;
2200
+ }
2201
+ if (view === "entities") {
2202
+ return current && next.nodes.some((node) => node.id === current.id) ? currentInResults : null;
2203
+ }
2204
+ return current && next.nodes.some((node) => node.id === current.id) ? currentInResults : next.nodes[0] ?? null;
2205
+ });
2049
2206
  } catch (cause) {
2050
2207
  setError(cause instanceof Error ? cause.message : "Failed to load brain");
2051
2208
  setResponse(null);
@@ -2062,7 +2219,8 @@ function BrainPage({ context }) {
2062
2219
  layer,
2063
2220
  search,
2064
2221
  searchMode,
2065
- status
2222
+ status,
2223
+ view
2066
2224
  ]);
2067
2225
  React7.useEffect(() => {
2068
2226
  void loadBrain();
@@ -2071,14 +2229,21 @@ function BrainPage({ context }) {
2071
2229
  const matches = data.matches ?? {};
2072
2230
  const related = data.related ?? [];
2073
2231
  const similar = data.similar ?? [];
2232
+ const entityCount = data.stats.byKind.entity ?? data.nodes.filter((node) => node.kind === "entity").length;
2074
2233
  const knowledgeCount = data.stats.byLayer.knowledge ?? 0;
2075
2234
  const workingCount = data.stats.byLayer.working ?? 0;
2076
2235
  const selectNode = React7.useCallback((node) => {
2077
2236
  setSelectedNode(node);
2078
2237
  setFocusNodeId(node.id);
2079
2238
  }, []);
2239
+ const selectView = React7.useCallback((nextView) => {
2240
+ setView(nextView);
2241
+ setFocusNodeId(null);
2242
+ setSelectedNode(null);
2243
+ }, []);
2080
2244
  const runSearch = React7.useCallback(() => {
2081
2245
  setFocusNodeId(null);
2246
+ setSelectedNode(null);
2082
2247
  void loadBrain(null);
2083
2248
  }, [loadBrain]);
2084
2249
  return /* @__PURE__ */ jsxs13("div", { className: "space-y-4", children: [
@@ -2114,16 +2279,16 @@ function BrainPage({ context }) {
2114
2279
  {
2115
2280
  items: [
2116
2281
  {
2117
- label: "Brain nodes",
2118
- value: data.stats.total,
2119
- detail: `${data.pageInfo.returned} shown`,
2120
- icon: Brain2
2282
+ label: "Entities",
2283
+ value: entityCount,
2284
+ detail: view === "entities" ? `${data.pageInfo.returned} shown` : "Stable anchors",
2285
+ icon: UsersRound
2121
2286
  },
2122
2287
  {
2123
2288
  label: "Knowledge",
2124
2289
  value: knowledgeCount,
2125
2290
  detail: "Durable nodes",
2126
- icon: CircleDot
2291
+ icon: BookOpen
2127
2292
  },
2128
2293
  {
2129
2294
  label: "Working",
@@ -2132,14 +2297,27 @@ function BrainPage({ context }) {
2132
2297
  icon: Sparkles
2133
2298
  },
2134
2299
  {
2135
- label: "Clusters",
2136
- value: data.clusters.length,
2137
- detail: "Layer and kind",
2138
- icon: Network
2300
+ label: selectedNode ? "Focus links" : "Brain nodes",
2301
+ value: selectedNode ? related.length + similar.length : data.stats.total,
2302
+ detail: selectedNode ? "Related and similar" : `${data.pageInfo.returned} shown`,
2303
+ icon: selectedNode ? GitBranch : Brain2
2139
2304
  }
2140
2305
  ]
2141
2306
  }
2142
2307
  ),
2308
+ /* @__PURE__ */ jsx21("div", { className: "inline-flex max-w-full overflow-hidden rounded-md border bg-background", children: BRAIN_VIEWS.map((item) => /* @__PURE__ */ jsx21(
2309
+ "button",
2310
+ {
2311
+ className: cn(
2312
+ "min-w-0 px-3 py-2 text-xs transition-colors sm:px-4",
2313
+ view === item ? "bg-primary text-primary-foreground" : "text-muted-foreground hover:bg-muted"
2314
+ ),
2315
+ onClick: () => selectView(item),
2316
+ type: "button",
2317
+ children: BRAIN_VIEW_LABELS[item]
2318
+ },
2319
+ item
2320
+ )) }),
2143
2321
  /* @__PURE__ */ jsxs13(
2144
2322
  FilterBar,
2145
2323
  {
@@ -2157,7 +2335,7 @@ function BrainPage({ context }) {
2157
2335
  }
2158
2336
  ),
2159
2337
  onSearchChange: setSearch,
2160
- searchPlaceholder: "Search brain",
2338
+ searchPlaceholder: view === "entities" ? "Search entities" : "Search brain",
2161
2339
  searchValue: search,
2162
2340
  children: [
2163
2341
  /* @__PURE__ */ jsx21("div", { className: "inline-flex h-8 overflow-hidden rounded-md border bg-background", children: BRAIN_SEARCH_MODES.map((mode) => /* @__PURE__ */ jsx21(
@@ -2173,17 +2351,24 @@ function BrainPage({ context }) {
2173
2351
  },
2174
2352
  mode
2175
2353
  )) }),
2176
- /* @__PURE__ */ jsxs13(
2354
+ view === "map" || view === "all" ? /* @__PURE__ */ jsxs13(
2177
2355
  Select,
2178
2356
  {
2179
2357
  value: layer,
2180
2358
  onValueChange: (value) => setLayer(value),
2181
2359
  children: [
2182
- /* @__PURE__ */ jsx21(SelectTrigger, { className: "h-8 w-[140px] text-xs", "aria-label": "Layer", children: /* @__PURE__ */ jsx21(SelectValue, {}) }),
2360
+ /* @__PURE__ */ jsx21(
2361
+ SelectTrigger,
2362
+ {
2363
+ className: "h-8 w-[140px] text-xs",
2364
+ "aria-label": "Layer",
2365
+ children: /* @__PURE__ */ jsx21(SelectValue, {})
2366
+ }
2367
+ ),
2183
2368
  /* @__PURE__ */ jsx21(SelectContent, { children: BRAIN_LAYERS.map((option) => /* @__PURE__ */ jsx21(SelectItem, { value: option, children: option === "all" ? "All layers" : formatLabel(option) }, option)) })
2184
2369
  ]
2185
2370
  }
2186
- ),
2371
+ ) : null,
2187
2372
  /* @__PURE__ */ jsxs13(
2188
2373
  Select,
2189
2374
  {
@@ -2195,17 +2380,24 @@ function BrainPage({ context }) {
2195
2380
  ]
2196
2381
  }
2197
2382
  ),
2198
- /* @__PURE__ */ jsxs13(
2383
+ view !== "entities" ? /* @__PURE__ */ jsxs13(
2199
2384
  Select,
2200
2385
  {
2201
2386
  value: kind,
2202
2387
  onValueChange: (value) => setKind(value),
2203
2388
  children: [
2204
- /* @__PURE__ */ jsx21(SelectTrigger, { className: "h-8 w-[170px] text-xs", "aria-label": "Kind", children: /* @__PURE__ */ jsx21(SelectValue, {}) }),
2389
+ /* @__PURE__ */ jsx21(
2390
+ SelectTrigger,
2391
+ {
2392
+ className: "h-8 w-[170px] text-xs",
2393
+ "aria-label": "Kind",
2394
+ children: /* @__PURE__ */ jsx21(SelectValue, {})
2395
+ }
2396
+ ),
2205
2397
  /* @__PURE__ */ jsx21(SelectContent, { children: BRAIN_KINDS.map((option) => /* @__PURE__ */ jsx21(SelectItem, { value: option, children: option === "all" ? "All kinds" : formatLabel(option) }, option)) })
2206
2398
  ]
2207
2399
  }
2208
- ),
2400
+ ) : /* @__PURE__ */ jsx21(Badge, { className: "h-8 px-3", variant: "secondary", children: "Entity index" }),
2209
2401
  /* @__PURE__ */ jsx21(
2210
2402
  Input,
2211
2403
  {
@@ -2245,72 +2437,299 @@ function BrainPage({ context }) {
2245
2437
  description: "Select a node from the map or table."
2246
2438
  }
2247
2439
  ),
2248
- children: /* @__PURE__ */ jsxs13("div", { className: "space-y-4", children: [
2440
+ children: /* @__PURE__ */ jsx21("div", { className: "space-y-4", children: /* @__PURE__ */ jsx21(
2441
+ BrainPrimaryContent,
2442
+ {
2443
+ data,
2444
+ loading,
2445
+ matches,
2446
+ onSelectNode: selectNode,
2447
+ related,
2448
+ selectedNode,
2449
+ similar,
2450
+ view
2451
+ }
2452
+ ) })
2453
+ }
2454
+ )
2455
+ ] });
2456
+ }
2457
+ function BrainPrimaryContent({
2458
+ data,
2459
+ loading,
2460
+ matches,
2461
+ onSelectNode,
2462
+ related,
2463
+ selectedNode,
2464
+ similar,
2465
+ view
2466
+ }) {
2467
+ if (view === "entities") {
2468
+ return /* @__PURE__ */ jsxs13("div", { className: "space-y-4", children: [
2469
+ /* @__PURE__ */ jsx21(
2470
+ EntityEgoGraph,
2471
+ {
2472
+ entity: selectedNode?.kind === "entity" ? selectedNode : null,
2473
+ loading,
2474
+ onSelectNode,
2475
+ related,
2476
+ similar
2477
+ }
2478
+ ),
2479
+ /* @__PURE__ */ jsx21(
2480
+ BrainNodeTable,
2481
+ {
2482
+ emptyDescription: "Entity nodes will appear here once the Brain has named people, tenants, projects, products, tools, policies, or concepts.",
2483
+ emptyTitle: loading ? "Loading entities" : "No entities yet",
2484
+ loading,
2485
+ matches,
2486
+ nodes: data.nodes,
2487
+ onSelectNode,
2488
+ variant: "entities"
2489
+ }
2490
+ )
2491
+ ] });
2492
+ }
2493
+ if (view === "map") {
2494
+ return /* @__PURE__ */ jsxs13("div", { className: "space-y-4", children: [
2495
+ /* @__PURE__ */ jsx21(
2496
+ BrainMap,
2497
+ {
2498
+ clusters: data.clusters,
2499
+ edges: data.edges,
2500
+ loading,
2501
+ nodes: data.nodes,
2502
+ onSelectNode,
2503
+ similar,
2504
+ selectedNodeId: selectedNode?.id ?? null
2505
+ }
2506
+ ),
2507
+ /* @__PURE__ */ jsx21(
2508
+ BrainNodeTable,
2509
+ {
2510
+ emptyDescription: "Knowledge and working-state nodes will appear here.",
2511
+ emptyTitle: loading ? "Loading brain" : "No brain nodes",
2512
+ loading,
2513
+ matches,
2514
+ nodes: data.nodes,
2515
+ onSelectNode
2516
+ }
2517
+ )
2518
+ ] });
2519
+ }
2520
+ return /* @__PURE__ */ jsx21(
2521
+ BrainNodeTable,
2522
+ {
2523
+ emptyDescription: view === "work" ? "Working-state nodes will appear here when the Brain captures current challenges, tasks, risks, and open questions." : view === "knowledge" ? "Durable facts, decisions, preferences, constraints, and entities will appear here." : "Knowledge and working-state nodes will appear here.",
2524
+ emptyTitle: loading ? `Loading ${BRAIN_VIEW_LABELS[view].toLowerCase()}` : `No ${BRAIN_VIEW_LABELS[view].toLowerCase()}`,
2525
+ loading,
2526
+ matches,
2527
+ nodes: data.nodes,
2528
+ onSelectNode
2529
+ }
2530
+ );
2531
+ }
2532
+ function BrainNodeTable({
2533
+ emptyDescription,
2534
+ emptyTitle,
2535
+ loading,
2536
+ matches,
2537
+ nodes,
2538
+ onSelectNode,
2539
+ variant = "default"
2540
+ }) {
2541
+ return /* @__PURE__ */ jsx21(
2542
+ ResourceTable,
2543
+ {
2544
+ rows: nodes,
2545
+ getRowKey: (row) => row.id,
2546
+ onRowClick: onSelectNode,
2547
+ empty: /* @__PURE__ */ jsx21(
2548
+ EmptyState,
2549
+ {
2550
+ icon: variant === "entities" ? UsersRound : Brain2,
2551
+ title: emptyTitle,
2552
+ description: loading ? "Fetching Brain nodes for the active namespace." : emptyDescription
2553
+ }
2554
+ ),
2555
+ columns: [
2556
+ {
2557
+ id: "node",
2558
+ header: variant === "entities" ? "Entity" : "Node",
2559
+ className: "max-w-[420px]",
2560
+ render: (row) => /* @__PURE__ */ jsxs13("div", { className: "min-w-0", children: [
2561
+ /* @__PURE__ */ jsx21("div", { className: "truncate font-medium", children: row.name }),
2562
+ /* @__PURE__ */ jsx21("div", { className: "truncate text-xs text-muted-foreground", children: row.content || "-" }),
2563
+ /* @__PURE__ */ jsx21(MatchBadges, { match: matches[row.id] })
2564
+ ] })
2565
+ },
2566
+ ...variant === "entities" ? [{
2567
+ id: "updated",
2568
+ header: "Updated",
2569
+ className: "whitespace-nowrap text-xs",
2570
+ render: (row) => formatDateTime(row.updatedAt)
2571
+ }] : [{
2572
+ id: "layer",
2573
+ header: "Layer",
2574
+ render: (row) => /* @__PURE__ */ jsx21(LayerBadge, { layer: row.layer })
2575
+ }, {
2576
+ id: "kind",
2577
+ header: "Kind",
2578
+ render: (row) => formatLabel(row.kind)
2579
+ }],
2580
+ {
2581
+ id: "status",
2582
+ header: "Status",
2583
+ render: (row) => /* @__PURE__ */ jsx21(StatusBadge, { status: row.status })
2584
+ },
2585
+ {
2586
+ id: "agent",
2587
+ header: "Agent",
2588
+ className: "max-w-[160px] truncate font-mono text-xs",
2589
+ render: (row) => row.agentId ?? "-"
2590
+ }
2591
+ ]
2592
+ }
2593
+ );
2594
+ }
2595
+ function EntityEgoGraph({
2596
+ entity,
2597
+ loading,
2598
+ onSelectNode,
2599
+ related,
2600
+ similar
2601
+ }) {
2602
+ if (!entity) {
2603
+ return /* @__PURE__ */ jsx21("div", { className: "flex min-h-[300px] items-center justify-center rounded-lg border bg-background", children: /* @__PURE__ */ jsx21(
2604
+ EmptyState,
2605
+ {
2606
+ icon: UsersRound,
2607
+ title: loading ? "Loading entity workspace" : "Select an entity",
2608
+ description: loading ? "Fetching entity nodes for the active namespace." : "Choose an entity to see its explicit relations and semantic neighbors."
2609
+ }
2610
+ ) });
2611
+ }
2612
+ const relatedItems = related.slice(0, 18);
2613
+ const similarItems = similar.slice(0, 10);
2614
+ const orbitCount = relatedItems.length + similarItems.length;
2615
+ const center = { x: 500, y: 260 };
2616
+ const radius = orbitCount > 12 ? 190 : 165;
2617
+ const orbitNodes = [
2618
+ ...relatedItems.map((item, index) => ({
2619
+ id: item.edge.id,
2620
+ index,
2621
+ node: item.node,
2622
+ tone: "relation",
2623
+ label: formatLabel(item.edge.type)
2624
+ })),
2625
+ ...similarItems.map((item, index) => ({
2626
+ id: `similar-${item.node.id}`,
2627
+ index: index + relatedItems.length,
2628
+ node: item.node,
2629
+ tone: "similar",
2630
+ label: formatSimilarityScore(item.similarity)
2631
+ }))
2632
+ ].map((item) => {
2633
+ const angle = orbitCount > 0 ? Math.PI * 2 * item.index / orbitCount - Math.PI / 2 : 0;
2634
+ return {
2635
+ ...item,
2636
+ x: center.x + Math.cos(angle) * radius,
2637
+ y: center.y + Math.sin(angle) * radius
2638
+ };
2639
+ });
2640
+ return /* @__PURE__ */ jsx21("div", { className: "overflow-hidden rounded-lg border bg-background", children: /* @__PURE__ */ jsxs13("div", { className: "grid gap-0 lg:grid-cols-[minmax(0,1.5fr)_320px]", children: [
2641
+ /* @__PURE__ */ jsxs13(
2642
+ "svg",
2643
+ {
2644
+ className: "block h-[380px] w-full bg-muted/20",
2645
+ role: "img",
2646
+ viewBox: "0 0 1000 520",
2647
+ children: [
2648
+ /* @__PURE__ */ jsx21("rect", { fill: "transparent", height: "520", width: "1000" }),
2649
+ orbitNodes.map((item) => /* @__PURE__ */ jsx21(
2650
+ "line",
2651
+ {
2652
+ stroke: item.tone === "similar" ? "#0ea5e9" : "#64748b",
2653
+ strokeDasharray: item.tone === "similar" ? "6 7" : void 0,
2654
+ strokeOpacity: item.tone === "similar" ? "0.48" : "0.38",
2655
+ strokeWidth: item.tone === "similar" ? "1.8" : "2.2",
2656
+ x1: center.x,
2657
+ x2: item.x,
2658
+ y1: center.y,
2659
+ y2: item.y
2660
+ },
2661
+ `line-${item.id}`
2662
+ )),
2249
2663
  /* @__PURE__ */ jsx21(
2250
- BrainMap,
2664
+ "circle",
2251
2665
  {
2252
- clusters: data.clusters,
2253
- edges: data.edges,
2254
- loading,
2255
- nodes: data.nodes,
2256
- onSelectNode: selectNode,
2257
- similar,
2258
- selectedNodeId: selectedNode?.id ?? null
2666
+ cx: center.x,
2667
+ cy: center.y,
2668
+ fill: nodeColor(entity),
2669
+ r: "22",
2670
+ stroke: "#0f172a",
2671
+ strokeWidth: "3",
2672
+ children: /* @__PURE__ */ jsx21("title", { children: entity.name })
2259
2673
  }
2260
2674
  ),
2261
- /* @__PURE__ */ jsx21(
2262
- ResourceTable,
2675
+ orbitNodes.map((item) => /* @__PURE__ */ jsx21(
2676
+ "g",
2263
2677
  {
2264
- rows: data.nodes,
2265
- getRowKey: (row) => row.id,
2266
- onRowClick: selectNode,
2267
- empty: /* @__PURE__ */ jsx21(
2268
- EmptyState,
2269
- {
2270
- icon: Brain2,
2271
- title: loading ? "Loading brain" : "No brain nodes",
2272
- description: loading ? "Fetching brain nodes for the active namespace." : "Knowledge and working-state nodes will appear here."
2273
- }
2274
- ),
2275
- columns: [
2276
- {
2277
- id: "node",
2278
- header: "Node",
2279
- className: "max-w-[360px]",
2280
- render: (row) => /* @__PURE__ */ jsxs13("div", { className: "min-w-0", children: [
2281
- /* @__PURE__ */ jsx21("div", { className: "truncate font-medium", children: row.name }),
2282
- /* @__PURE__ */ jsx21("div", { className: "truncate text-xs text-muted-foreground", children: row.content || "-" }),
2283
- /* @__PURE__ */ jsx21(MatchBadges, { match: matches[row.id] })
2284
- ] })
2285
- },
2286
- {
2287
- id: "layer",
2288
- header: "Layer",
2289
- render: (row) => /* @__PURE__ */ jsx21(LayerBadge, { layer: row.layer })
2290
- },
2291
- {
2292
- id: "kind",
2293
- header: "Kind",
2294
- render: (row) => formatLabel(row.kind)
2295
- },
2296
- {
2297
- id: "status",
2298
- header: "Status",
2299
- render: (row) => /* @__PURE__ */ jsx21(StatusBadge, { status: row.status })
2300
- },
2678
+ className: "cursor-pointer",
2679
+ onClick: () => onSelectNode(item.node),
2680
+ children: /* @__PURE__ */ jsx21(
2681
+ "circle",
2301
2682
  {
2302
- id: "agent",
2303
- header: "Agent",
2304
- className: "max-w-[160px] truncate font-mono text-xs",
2305
- render: (row) => row.agentId ?? "-"
2683
+ cx: item.x,
2684
+ cy: item.y,
2685
+ fill: nodeColor(item.node),
2686
+ r: item.tone === "similar" ? 9 : 11,
2687
+ stroke: item.tone === "similar" ? "#0ea5e9" : "#ffffff",
2688
+ strokeWidth: "2",
2689
+ children: /* @__PURE__ */ jsx21("title", { children: `${item.node.name} \xB7 ${formatLabel(item.node.kind)} \xB7 ${item.label}` })
2306
2690
  }
2307
- ]
2308
- }
2309
- )
2310
- ] })
2691
+ )
2692
+ },
2693
+ item.id
2694
+ ))
2695
+ ]
2311
2696
  }
2312
- )
2313
- ] });
2697
+ ),
2698
+ /* @__PURE__ */ jsxs13("div", { className: "border-t p-4 lg:border-l lg:border-t-0", children: [
2699
+ /* @__PURE__ */ jsxs13("div", { className: "flex flex-wrap items-center gap-2", children: [
2700
+ /* @__PURE__ */ jsx21(LayerBadge, { layer: entity.layer }),
2701
+ /* @__PURE__ */ jsx21(StatusBadge, { status: entity.status }),
2702
+ /* @__PURE__ */ jsx21(Badge, { variant: "outline", children: "Entity" })
2703
+ ] }),
2704
+ /* @__PURE__ */ jsx21("h3", { className: "mt-3 truncate text-base font-semibold", children: entity.name }),
2705
+ /* @__PURE__ */ jsx21("p", { className: "mt-2 line-clamp-4 text-sm leading-6 text-muted-foreground", children: entity.content || "-" }),
2706
+ /* @__PURE__ */ jsxs13("div", { className: "mt-4 grid grid-cols-2 gap-2 text-xs", children: [
2707
+ /* @__PURE__ */ jsxs13("div", { className: "rounded-md border bg-muted/20 p-3", children: [
2708
+ /* @__PURE__ */ jsx21("div", { className: "text-muted-foreground", children: "Explicit" }),
2709
+ /* @__PURE__ */ jsx21("div", { className: "mt-1 text-lg font-semibold", children: related.length })
2710
+ ] }),
2711
+ /* @__PURE__ */ jsxs13("div", { className: "rounded-md border bg-muted/20 p-3", children: [
2712
+ /* @__PURE__ */ jsx21("div", { className: "text-muted-foreground", children: "Semantic" }),
2713
+ /* @__PURE__ */ jsx21("div", { className: "mt-1 text-lg font-semibold", children: similar.length })
2714
+ ] })
2715
+ ] }),
2716
+ /* @__PURE__ */ jsxs13("div", { className: "mt-4 space-y-2", children: [
2717
+ /* @__PURE__ */ jsx21("div", { className: "text-xs font-medium text-muted-foreground", children: "Knowledge orbit" }),
2718
+ groupBrainRelationsByKind(related).slice(0, 5).map((group) => /* @__PURE__ */ jsxs13(
2719
+ "div",
2720
+ {
2721
+ className: "flex items-center justify-between gap-3 text-xs",
2722
+ children: [
2723
+ /* @__PURE__ */ jsx21("span", { className: "truncate", children: group.label }),
2724
+ /* @__PURE__ */ jsx21(Badge, { variant: "secondary", children: group.items.length })
2725
+ ]
2726
+ },
2727
+ group.id
2728
+ )),
2729
+ related.length === 0 && similar.length === 0 ? /* @__PURE__ */ jsx21("p", { className: "text-xs leading-5 text-muted-foreground", children: "Select an entity with relations or embeddings to see its neighborhood." }) : null
2730
+ ] })
2731
+ ] })
2732
+ ] }) });
2314
2733
  }
2315
2734
  function BrainMap({
2316
2735
  clusters,
@@ -2442,6 +2861,11 @@ function BrainNodeInspector({
2442
2861
  similar
2443
2862
  }) {
2444
2863
  const [tab, setTab] = React7.useState("overview");
2864
+ const isEntity = node.kind === "entity";
2865
+ const tabs = isEntity ? ["overview", "knowledge", "work", "relations", "similar", "evidence", "json"] : ["overview", "relations", "similar", "evidence", "json"];
2866
+ React7.useEffect(() => {
2867
+ setTab("overview");
2868
+ }, [node.id]);
2445
2869
  return /* @__PURE__ */ jsxs13("div", { className: "space-y-3", children: [
2446
2870
  /* @__PURE__ */ jsxs13("div", { className: "rounded-lg border bg-background p-4", children: [
2447
2871
  /* @__PURE__ */ jsxs13("div", { className: "flex flex-wrap items-center gap-2", children: [
@@ -2453,7 +2877,7 @@ function BrainNodeInspector({
2453
2877
  /* @__PURE__ */ jsx21("p", { className: "mt-2 text-sm leading-6 text-muted-foreground", children: node.content || "-" }),
2454
2878
  /* @__PURE__ */ jsx21(MatchBadges, { match })
2455
2879
  ] }),
2456
- /* @__PURE__ */ jsx21("div", { className: "inline-flex w-full overflow-hidden rounded-md border bg-background", children: ["overview", "related", "similar", "evidence", "json"].map((item) => /* @__PURE__ */ jsx21(
2880
+ /* @__PURE__ */ jsx21("div", { className: "inline-flex w-full overflow-hidden rounded-md border bg-background", children: tabs.map((item) => /* @__PURE__ */ jsx21(
2457
2881
  "button",
2458
2882
  {
2459
2883
  className: cn(
@@ -2467,7 +2891,33 @@ function BrainNodeInspector({
2467
2891
  item
2468
2892
  )) }),
2469
2893
  tab === "overview" ? /* @__PURE__ */ jsx21(BrainOverview, { node }) : null,
2470
- tab === "related" ? /* @__PURE__ */ jsx21(RelatedPanel, { onSelectNode, related }) : null,
2894
+ tab === "knowledge" ? /* @__PURE__ */ jsx21(
2895
+ RelationGroupsPanel,
2896
+ {
2897
+ emptyDescription: "Durable decisions, facts, preferences, and constraints related to this entity will appear here.",
2898
+ emptyTitle: "No durable knowledge",
2899
+ groups: getKnowledgeRelationGroups(related),
2900
+ onSelectNode
2901
+ }
2902
+ ) : null,
2903
+ tab === "work" ? /* @__PURE__ */ jsx21(
2904
+ RelationGroupsPanel,
2905
+ {
2906
+ emptyDescription: "Tasks, risks, open questions, and current-state nodes related to this entity will appear here.",
2907
+ emptyTitle: "No current work",
2908
+ groups: getWorkRelationGroups(related),
2909
+ onSelectNode
2910
+ }
2911
+ ) : null,
2912
+ tab === "relations" ? /* @__PURE__ */ jsx21(
2913
+ RelationGroupsPanel,
2914
+ {
2915
+ emptyDescription: "Explicit relation edges will appear here when this node is connected to other concepts.",
2916
+ emptyTitle: "No explicit relations",
2917
+ groups: groupBrainRelationsByKind(related),
2918
+ onSelectNode
2919
+ }
2920
+ ) : null,
2471
2921
  tab === "similar" ? /* @__PURE__ */ jsx21(SimilarPanel, { onSelectNode, similar }) : null,
2472
2922
  tab === "evidence" ? /* @__PURE__ */ jsx21(EvidencePanel, { node }) : null,
2473
2923
  tab === "json" ? /* @__PURE__ */ jsx21(JsonPanel, { title: "Node JSON", value: node, minHeight: 300 }) : null
@@ -2484,37 +2934,49 @@ function BrainOverview({ node }) {
2484
2934
  /* @__PURE__ */ jsx21(InspectorRow, { label: "Updated", value: formatDateTime(node.updatedAt) })
2485
2935
  ] }) });
2486
2936
  }
2487
- function RelatedPanel({
2488
- onSelectNode,
2489
- related
2937
+ function RelationGroupsPanel({
2938
+ emptyDescription,
2939
+ emptyTitle,
2940
+ groups,
2941
+ onSelectNode
2490
2942
  }) {
2491
- if (related.length === 0) {
2943
+ if (groups.length === 0) {
2492
2944
  return /* @__PURE__ */ jsx21(
2493
2945
  EmptyState,
2494
2946
  {
2495
2947
  icon: GitBranch,
2496
- title: "No explicit relations",
2497
- description: "Relation edges will appear here when this node is connected to other concepts."
2948
+ title: emptyTitle,
2949
+ description: emptyDescription
2498
2950
  }
2499
2951
  );
2500
2952
  }
2501
- return /* @__PURE__ */ jsx21("div", { className: "space-y-2", children: related.map((item) => /* @__PURE__ */ jsxs13(
2502
- "button",
2503
- {
2504
- className: "w-full rounded-lg border bg-background p-3 text-left transition-colors hover:bg-muted/40",
2505
- onClick: () => onSelectNode(item.node),
2506
- type: "button",
2507
- children: [
2508
- /* @__PURE__ */ jsxs13("div", { className: "flex flex-wrap items-center gap-2", children: [
2509
- /* @__PURE__ */ jsx21(Badge, { variant: "outline", children: formatLabel(item.edge.type) }),
2510
- /* @__PURE__ */ jsx21(Badge, { variant: "secondary", children: item.direction === "out" ? "Outgoing" : "Incoming" })
2511
- ] }),
2512
- /* @__PURE__ */ jsx21("div", { className: "mt-2 text-sm font-medium", children: item.node.name }),
2513
- /* @__PURE__ */ jsx21("p", { className: "mt-1 line-clamp-3 text-xs leading-5 text-muted-foreground", children: item.node.content || "-" })
2514
- ]
2515
- },
2516
- item.edge.id
2517
- )) });
2953
+ return /* @__PURE__ */ jsx21("div", { className: "space-y-3", children: groups.map((group) => /* @__PURE__ */ jsxs13("section", { className: "rounded-lg border bg-background", children: [
2954
+ /* @__PURE__ */ jsx21("div", { className: "border-b px-3 py-2", children: /* @__PURE__ */ jsxs13("div", { className: "flex items-center justify-between gap-3", children: [
2955
+ /* @__PURE__ */ jsxs13("div", { className: "min-w-0", children: [
2956
+ /* @__PURE__ */ jsx21("h4", { className: "truncate text-sm font-medium", children: group.label }),
2957
+ /* @__PURE__ */ jsx21("p", { className: "mt-0.5 line-clamp-2 text-xs text-muted-foreground", children: group.description })
2958
+ ] }),
2959
+ /* @__PURE__ */ jsx21(Badge, { variant: "secondary", children: group.items.length })
2960
+ ] }) }),
2961
+ /* @__PURE__ */ jsx21("div", { className: "divide-y", children: group.items.map((item) => /* @__PURE__ */ jsxs13(
2962
+ "button",
2963
+ {
2964
+ className: "w-full p-3 text-left transition-colors hover:bg-muted/40",
2965
+ onClick: () => onSelectNode(item.node),
2966
+ type: "button",
2967
+ children: [
2968
+ /* @__PURE__ */ jsxs13("div", { className: "flex flex-wrap items-center gap-2", children: [
2969
+ /* @__PURE__ */ jsx21(Badge, { variant: "outline", children: formatLabel(item.edge.type) }),
2970
+ /* @__PURE__ */ jsx21(Badge, { variant: "secondary", children: item.direction === "out" ? "Outgoing" : "Incoming" }),
2971
+ /* @__PURE__ */ jsx21(LayerBadge, { layer: item.node.layer })
2972
+ ] }),
2973
+ /* @__PURE__ */ jsx21("div", { className: "mt-2 text-sm font-medium", children: item.node.name }),
2974
+ /* @__PURE__ */ jsx21("p", { className: "mt-1 line-clamp-3 text-xs leading-5 text-muted-foreground", children: item.node.content || "-" })
2975
+ ]
2976
+ },
2977
+ item.edge.id
2978
+ )) })
2979
+ ] }, group.id)) });
2518
2980
  }
2519
2981
  function SimilarPanel({
2520
2982
  onSelectNode,
@@ -2550,6 +3012,7 @@ function SimilarPanel({
2550
3012
  )) });
2551
3013
  }
2552
3014
  function EvidencePanel({ node }) {
3015
+ const sourceMessageIds = Array.isArray(node.sourceMessageIds) ? node.sourceMessageIds : [];
2553
3016
  return /* @__PURE__ */ jsxs13("div", { className: "rounded-lg border bg-background p-4", children: [
2554
3017
  /* @__PURE__ */ jsxs13("dl", { className: "grid gap-2 text-xs", children: [
2555
3018
  /* @__PURE__ */ jsx21(InspectorRow, { label: "Source", value: node.sourceType }),
@@ -2559,7 +3022,7 @@ function EvidencePanel({ node }) {
2559
3022
  ] }),
2560
3023
  /* @__PURE__ */ jsxs13("div", { className: "mt-4", children: [
2561
3024
  /* @__PURE__ */ jsx21("div", { className: "text-xs font-medium text-muted-foreground", children: "Source messages" }),
2562
- /* @__PURE__ */ jsx21("div", { className: "mt-2 flex flex-wrap gap-1", children: node.sourceMessageIds.length ? node.sourceMessageIds.map((id) => /* @__PURE__ */ jsx21(Badge, { className: "max-w-full truncate", variant: "outline", children: id }, id)) : /* @__PURE__ */ jsx21("span", { className: "text-xs text-muted-foreground", children: "-" }) })
3025
+ /* @__PURE__ */ jsx21("div", { className: "mt-2 flex flex-wrap gap-1", children: sourceMessageIds.length ? sourceMessageIds.map((id) => /* @__PURE__ */ jsx21(Badge, { className: "max-w-full truncate", variant: "outline", children: id }, id)) : /* @__PURE__ */ jsx21("span", { className: "text-xs text-muted-foreground", children: "-" }) })
2563
3026
  ] })
2564
3027
  ] });
2565
3028
  }
@@ -5010,8 +5473,11 @@ function useCopilotzAdmin(options = {}) {
5010
5473
  export {
5011
5474
  ADMIN_GROUP_LABELS,
5012
5475
  ADMIN_GROUP_ORDER,
5476
+ BRAIN_RELATION_GROUP_DEFINITIONS,
5477
+ BRAIN_VIEW_LABELS,
5013
5478
  CopilotzAdmin,
5014
5479
  EMPTY_USAGE_TOTALS,
5480
+ ENTITY_FOCUS_RELATION_TYPES,
5015
5481
  EmptyState,
5016
5482
  FilterBar,
5017
5483
  InspectorPanel,
@@ -5040,11 +5506,16 @@ export {
5040
5506
  formatNumber,
5041
5507
  formatPercent,
5042
5508
  formatUsageBucket,
5509
+ getBrainViewBaseFilters,
5510
+ getKnowledgeRelationGroups,
5043
5511
  getUsageDimensionLabel,
5044
5512
  getUsageGroupLabel,
5045
5513
  getUsageMetricLabel,
5046
5514
  getUsageRange,
5047
5515
  getUsageTotalValue,
5516
+ getWorkRelationGroups,
5517
+ groupBrainRelationsByKind,
5518
+ isEntityBrainView,
5048
5519
  mergeAdminConfig,
5049
5520
  overviewModule,
5050
5521
  participantsModule,