@copilotz/admin 0.9.39 → 0.9.40

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.cjs CHANGED
@@ -313,6 +313,14 @@ function createAdminClient(options = {}) {
313
313
  kind: filters.kind === "all" ? void 0 : filters.kind,
314
314
  status: filters.status === "all" ? void 0 : filters.status,
315
315
  search: filters.search,
316
+ searchMode: filters.searchMode,
317
+ focusNodeId: filters.focusNodeId,
318
+ includeRelated: filters.includeRelated ? "true" : void 0,
319
+ includeSimilar: filters.includeSimilar ? "true" : void 0,
320
+ similarLimit: filters.similarLimit ? String(filters.similarLimit) : void 0,
321
+ minSimilarity: typeof filters.minSimilarity === "number" ? String(filters.minSimilarity) : void 0,
322
+ relationDepth: filters.relationDepth ? String(filters.relationDepth) : void 0,
323
+ relationTypes: filters.relationTypes?.join(","),
316
324
  limit: String(filters.limit ?? 160),
317
325
  offset: filters.offset ? String(filters.offset) : void 0
318
326
  }),
@@ -2023,6 +2031,7 @@ var import_react3 = __toESM(require("react"), 1);
2023
2031
  var import_lucide_react7 = require("lucide-react");
2024
2032
  var import_jsx_runtime21 = require("react/jsx-runtime");
2025
2033
  var BRAIN_LAYERS = ["all", "knowledge", "working"];
2034
+ var BRAIN_SEARCH_MODES = ["hybrid", "semantic", "keyword"];
2026
2035
  var BRAIN_STATUSES = ["active", "all", "superseded", "archived"];
2027
2036
  var BRAIN_KINDS = [
2028
2037
  "all",
@@ -2060,6 +2069,9 @@ function brainModule() {
2060
2069
  }
2061
2070
  function BrainPage({ context }) {
2062
2071
  const [search, setSearch] = import_react3.default.useState("");
2072
+ const [searchMode, setSearchMode] = import_react3.default.useState(
2073
+ "hybrid"
2074
+ );
2063
2075
  const [layer, setLayer] = import_react3.default.useState("all");
2064
2076
  const [status, setStatus] = import_react3.default.useState(
2065
2077
  "active"
@@ -2072,24 +2084,32 @@ function BrainPage({ context }) {
2072
2084
  const [selectedNode, setSelectedNode] = import_react3.default.useState(
2073
2085
  null
2074
2086
  );
2087
+ const [focusNodeId, setFocusNodeId] = import_react3.default.useState(null);
2075
2088
  const [loading, setLoading] = import_react3.default.useState(false);
2076
2089
  const [error, setError] = import_react3.default.useState(null);
2077
- const loadBrain = import_react3.default.useCallback(async () => {
2090
+ const loadBrain = import_react3.default.useCallback(async (focusOverride) => {
2078
2091
  setLoading(true);
2079
2092
  setError(null);
2093
+ const effectiveFocusNodeId = focusOverride === void 0 ? focusNodeId : focusOverride;
2080
2094
  try {
2081
2095
  const next = await context.client.getBrain({
2082
2096
  namespace: context.scope.namespace || void 0,
2083
2097
  search: search.trim() || void 0,
2098
+ searchMode,
2084
2099
  layer,
2085
2100
  status,
2086
2101
  kind,
2087
2102
  agentId: agentId.trim() || void 0,
2103
+ focusNodeId: effectiveFocusNodeId || void 0,
2104
+ includeRelated: Boolean(effectiveFocusNodeId),
2105
+ includeSimilar: Boolean(effectiveFocusNodeId),
2106
+ similarLimit: 24,
2107
+ minSimilarity: 0.2,
2088
2108
  limit: 180
2089
2109
  });
2090
2110
  setResponse(next);
2091
2111
  setSelectedNode(
2092
- (current) => current && next.nodes.some((node) => node.id === current.id) ? current : next.nodes[0] ?? null
2112
+ (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
2093
2113
  );
2094
2114
  } catch (cause) {
2095
2115
  setError(cause instanceof Error ? cause.message : "Failed to load brain");
@@ -2102,17 +2122,30 @@ function BrainPage({ context }) {
2102
2122
  agentId,
2103
2123
  context.client,
2104
2124
  context.scope.namespace,
2125
+ focusNodeId,
2105
2126
  kind,
2106
2127
  layer,
2107
2128
  search,
2129
+ searchMode,
2108
2130
  status
2109
2131
  ]);
2110
2132
  import_react3.default.useEffect(() => {
2111
2133
  void loadBrain();
2112
- }, [context.refreshKey, context.scope.namespace, loadBrain]);
2134
+ }, [context.refreshKey, context.scope.namespace, focusNodeId, loadBrain]);
2113
2135
  const data = response ?? emptyBrainResponse();
2136
+ const matches = data.matches ?? {};
2137
+ const related = data.related ?? [];
2138
+ const similar = data.similar ?? [];
2114
2139
  const knowledgeCount = data.stats.byLayer.knowledge ?? 0;
2115
2140
  const workingCount = data.stats.byLayer.working ?? 0;
2141
+ const selectNode = import_react3.default.useCallback((node) => {
2142
+ setSelectedNode(node);
2143
+ setFocusNodeId(node.id);
2144
+ }, []);
2145
+ const runSearch = import_react3.default.useCallback(() => {
2146
+ setFocusNodeId(null);
2147
+ void loadBrain(null);
2148
+ }, [loadBrain]);
2116
2149
  return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "space-y-4", children: [
2117
2150
  /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
2118
2151
  PageHeader,
@@ -2179,7 +2212,7 @@ function BrainPage({ context }) {
2179
2212
  Button,
2180
2213
  {
2181
2214
  disabled: loading,
2182
- onClick: () => void loadBrain(),
2215
+ onClick: runSearch,
2183
2216
  size: "sm",
2184
2217
  type: "button",
2185
2218
  children: [
@@ -2192,6 +2225,19 @@ function BrainPage({ context }) {
2192
2225
  searchPlaceholder: "Search brain",
2193
2226
  searchValue: search,
2194
2227
  children: [
2228
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "inline-flex h-8 overflow-hidden rounded-md border bg-background", children: BRAIN_SEARCH_MODES.map((mode) => /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
2229
+ "button",
2230
+ {
2231
+ className: cn(
2232
+ "px-3 text-xs transition-colors",
2233
+ searchMode === mode ? "bg-primary text-primary-foreground" : "text-muted-foreground hover:bg-muted"
2234
+ ),
2235
+ onClick: () => setSearchMode(mode),
2236
+ type: "button",
2237
+ children: formatLabel(mode)
2238
+ },
2239
+ mode
2240
+ )) }),
2195
2241
  /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
2196
2242
  Select,
2197
2243
  {
@@ -2231,7 +2277,7 @@ function BrainPage({ context }) {
2231
2277
  className: "h-8 w-[190px]",
2232
2278
  onChange: (event) => setAgentId(event.target.value),
2233
2279
  onKeyDown: (event) => {
2234
- if (event.key === "Enter") void loadBrain();
2280
+ if (event.key === "Enter") runSearch();
2235
2281
  },
2236
2282
  placeholder: "Agent ID",
2237
2283
  value: agentId
@@ -2240,10 +2286,23 @@ function BrainPage({ context }) {
2240
2286
  ]
2241
2287
  }
2242
2288
  ),
2289
+ data.semantic?.requested && data.semantic.error ? /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "rounded-md border border-destructive/30 bg-destructive/5 px-3 py-2 text-xs text-destructive", children: [
2290
+ "Semantic search unavailable: ",
2291
+ data.semantic.error
2292
+ ] }) : null,
2243
2293
  error ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(EmptyState, { title: "Unable to load brain", description: error }) : /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
2244
2294
  InspectorPanel,
2245
2295
  {
2246
- side: selectedNode ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(BrainNodeInspector, { node: selectedNode }) : /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
2296
+ side: selectedNode ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
2297
+ BrainNodeInspector,
2298
+ {
2299
+ match: matches[selectedNode.id],
2300
+ node: selectedNode,
2301
+ onSelectNode: selectNode,
2302
+ related,
2303
+ similar
2304
+ }
2305
+ ) : /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
2247
2306
  EmptyState,
2248
2307
  {
2249
2308
  icon: import_lucide_react7.Brain,
@@ -2259,7 +2318,8 @@ function BrainPage({ context }) {
2259
2318
  edges: data.edges,
2260
2319
  loading,
2261
2320
  nodes: data.nodes,
2262
- onSelectNode: setSelectedNode,
2321
+ onSelectNode: selectNode,
2322
+ similar,
2263
2323
  selectedNodeId: selectedNode?.id ?? null
2264
2324
  }
2265
2325
  ),
@@ -2268,7 +2328,7 @@ function BrainPage({ context }) {
2268
2328
  {
2269
2329
  rows: data.nodes,
2270
2330
  getRowKey: (row) => row.id,
2271
- onRowClick: setSelectedNode,
2331
+ onRowClick: selectNode,
2272
2332
  empty: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
2273
2333
  EmptyState,
2274
2334
  {
@@ -2284,7 +2344,8 @@ function BrainPage({ context }) {
2284
2344
  className: "max-w-[360px]",
2285
2345
  render: (row) => /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "min-w-0", children: [
2286
2346
  /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "truncate font-medium", children: row.name }),
2287
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "truncate text-xs text-muted-foreground", children: row.content || "-" })
2347
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "truncate text-xs text-muted-foreground", children: row.content || "-" }),
2348
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(MatchBadges, { match: matches[row.id] })
2288
2349
  ] })
2289
2350
  },
2290
2351
  {
@@ -2322,6 +2383,7 @@ function BrainMap({
2322
2383
  loading,
2323
2384
  nodes,
2324
2385
  onSelectNode,
2386
+ similar,
2325
2387
  selectedNodeId
2326
2388
  }) {
2327
2389
  const nodeById = import_react3.default.useMemo(
@@ -2374,12 +2436,15 @@ function BrainMap({
2374
2436
  const source = nodeById.get(edge.sourceNodeId);
2375
2437
  const target = nodeById.get(edge.targetNodeId);
2376
2438
  if (!source || !target) return null;
2439
+ const isSelectedEdge = Boolean(
2440
+ selectedNodeId && (edge.sourceNodeId === selectedNodeId || edge.targetNodeId === selectedNodeId)
2441
+ );
2377
2442
  return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
2378
2443
  "line",
2379
2444
  {
2380
2445
  stroke: edge.type === "contradicts" ? "#dc2626" : "#64748b",
2381
- strokeOpacity: "0.32",
2382
- strokeWidth: edge.type === "supports" ? 2 : 1.3,
2446
+ strokeOpacity: isSelectedEdge ? "0.75" : "0.32",
2447
+ strokeWidth: isSelectedEdge ? 2.6 : edge.type === "supports" ? 2 : 1.3,
2383
2448
  x1: source.x * 1e3,
2384
2449
  x2: target.x * 1e3,
2385
2450
  y1: source.y * 600,
@@ -2388,6 +2453,25 @@ function BrainMap({
2388
2453
  edge.id
2389
2454
  );
2390
2455
  }),
2456
+ selectedNodeId ? similar.map((item) => {
2457
+ const source = nodeById.get(selectedNodeId);
2458
+ const target = nodeById.get(item.node.id);
2459
+ if (!source || !target) return null;
2460
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
2461
+ "line",
2462
+ {
2463
+ stroke: "#0ea5e9",
2464
+ strokeDasharray: "5 6",
2465
+ strokeOpacity: "0.42",
2466
+ strokeWidth: "1.8",
2467
+ x1: source.x * 1e3,
2468
+ x2: target.x * 1e3,
2469
+ y1: source.y * 600,
2470
+ y2: target.y * 600
2471
+ },
2472
+ `similar-${item.node.id}`
2473
+ );
2474
+ }) : null,
2391
2475
  nodes.map((node) => {
2392
2476
  const isSelected = node.id === selectedNodeId;
2393
2477
  return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
@@ -2415,7 +2499,14 @@ function BrainMap({
2415
2499
  }
2416
2500
  ) });
2417
2501
  }
2418
- function BrainNodeInspector({ node }) {
2502
+ function BrainNodeInspector({
2503
+ match,
2504
+ node,
2505
+ onSelectNode,
2506
+ related,
2507
+ similar
2508
+ }) {
2509
+ const [tab, setTab] = import_react3.default.useState("overview");
2419
2510
  return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "space-y-3", children: [
2420
2511
  /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "rounded-lg border bg-background p-4", children: [
2421
2512
  /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex flex-wrap items-center gap-2", children: [
@@ -2425,24 +2516,122 @@ function BrainNodeInspector({ node }) {
2425
2516
  ] }),
2426
2517
  /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("h3", { className: "mt-3 text-base font-semibold", children: node.name }),
2427
2518
  /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("p", { className: "mt-2 text-sm leading-6 text-muted-foreground", children: node.content || "-" }),
2428
- /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("dl", { className: "mt-4 grid gap-2 text-xs", children: [
2429
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(InspectorRow, { label: "Agent", value: node.agentId }),
2430
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(InspectorRow, { label: "Thread", value: node.threadId }),
2431
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(InspectorRow, { label: "Memory space", value: node.memorySpaceId }),
2432
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(InspectorRow, { label: "Checkpoint", value: node.checkpointId }),
2433
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(InspectorRow, { label: "Source field", value: node.sourceField }),
2434
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
2435
- InspectorRow,
2436
- {
2437
- label: "Updated",
2438
- value: formatDateTime(node.updatedAt)
2439
- }
2440
- )
2441
- ] })
2519
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(MatchBadges, { match })
2442
2520
  ] }),
2443
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(JsonPanel, { title: "Node JSON", value: node, minHeight: 300 })
2521
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "inline-flex w-full overflow-hidden rounded-md border bg-background", children: ["overview", "related", "similar", "evidence", "json"].map((item) => /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
2522
+ "button",
2523
+ {
2524
+ className: cn(
2525
+ "min-w-0 flex-1 px-2 py-2 text-xs transition-colors",
2526
+ tab === item ? "bg-muted font-medium text-foreground" : "text-muted-foreground hover:bg-muted/60"
2527
+ ),
2528
+ onClick: () => setTab(item),
2529
+ type: "button",
2530
+ children: formatLabel(item)
2531
+ },
2532
+ item
2533
+ )) }),
2534
+ tab === "overview" ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(BrainOverview, { node }) : null,
2535
+ tab === "related" ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(RelatedPanel, { onSelectNode, related }) : null,
2536
+ tab === "similar" ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(SimilarPanel, { onSelectNode, similar }) : null,
2537
+ tab === "evidence" ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(EvidencePanel, { node }) : null,
2538
+ tab === "json" ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(JsonPanel, { title: "Node JSON", value: node, minHeight: 300 }) : null
2444
2539
  ] });
2445
2540
  }
2541
+ function BrainOverview({ node }) {
2542
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "rounded-lg border bg-background p-4", children: /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("dl", { className: "grid gap-2 text-xs", children: [
2543
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(InspectorRow, { label: "Agent", value: node.agentId }),
2544
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(InspectorRow, { label: "Thread", value: node.threadId }),
2545
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(InspectorRow, { label: "Memory space", value: node.memorySpaceId }),
2546
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(InspectorRow, { label: "Checkpoint", value: node.checkpointId }),
2547
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(InspectorRow, { label: "Source field", value: node.sourceField }),
2548
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(InspectorRow, { label: "Confidence", value: formatNullableNumber(node.confidence) }),
2549
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(InspectorRow, { label: "Updated", value: formatDateTime(node.updatedAt) })
2550
+ ] }) });
2551
+ }
2552
+ function RelatedPanel({
2553
+ onSelectNode,
2554
+ related
2555
+ }) {
2556
+ if (related.length === 0) {
2557
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
2558
+ EmptyState,
2559
+ {
2560
+ icon: import_lucide_react7.GitBranch,
2561
+ title: "No explicit relations",
2562
+ description: "Relation edges will appear here when this node is connected to other concepts."
2563
+ }
2564
+ );
2565
+ }
2566
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "space-y-2", children: related.map((item) => /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
2567
+ "button",
2568
+ {
2569
+ className: "w-full rounded-lg border bg-background p-3 text-left transition-colors hover:bg-muted/40",
2570
+ onClick: () => onSelectNode(item.node),
2571
+ type: "button",
2572
+ children: [
2573
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex flex-wrap items-center gap-2", children: [
2574
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(Badge, { variant: "outline", children: formatLabel(item.edge.type) }),
2575
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(Badge, { variant: "secondary", children: item.direction === "out" ? "Outgoing" : "Incoming" })
2576
+ ] }),
2577
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "mt-2 text-sm font-medium", children: item.node.name }),
2578
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("p", { className: "mt-1 line-clamp-3 text-xs leading-5 text-muted-foreground", children: item.node.content || "-" })
2579
+ ]
2580
+ },
2581
+ item.edge.id
2582
+ )) });
2583
+ }
2584
+ function SimilarPanel({
2585
+ onSelectNode,
2586
+ similar
2587
+ }) {
2588
+ if (similar.length === 0) {
2589
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
2590
+ EmptyState,
2591
+ {
2592
+ icon: import_lucide_react7.Sparkles,
2593
+ title: "No semantic neighbors",
2594
+ description: "Similar nodes appear here when the selected node has an embedding."
2595
+ }
2596
+ );
2597
+ }
2598
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "space-y-2", children: similar.map((item) => /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
2599
+ "button",
2600
+ {
2601
+ className: "w-full rounded-lg border bg-background p-3 text-left transition-colors hover:bg-muted/40",
2602
+ onClick: () => onSelectNode(item.node),
2603
+ type: "button",
2604
+ children: [
2605
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex flex-wrap items-center gap-2", children: [
2606
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(Badge, { variant: "secondary", children: formatSimilarityScore(item.similarity) }),
2607
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(LayerBadge, { layer: item.node.layer }),
2608
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(Badge, { variant: "outline", children: formatLabel(item.node.kind) })
2609
+ ] }),
2610
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "mt-2 text-sm font-medium", children: item.node.name }),
2611
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("p", { className: "mt-1 line-clamp-3 text-xs leading-5 text-muted-foreground", children: item.node.content || "-" })
2612
+ ]
2613
+ },
2614
+ item.node.id
2615
+ )) });
2616
+ }
2617
+ function EvidencePanel({ node }) {
2618
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "rounded-lg border bg-background p-4", children: [
2619
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("dl", { className: "grid gap-2 text-xs", children: [
2620
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(InspectorRow, { label: "Source", value: node.sourceType }),
2621
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(InspectorRow, { label: "Source ID", value: node.sourceId }),
2622
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(InspectorRow, { label: "Thread", value: node.threadId }),
2623
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(InspectorRow, { label: "Checkpoint", value: node.checkpointId })
2624
+ ] }),
2625
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "mt-4", children: [
2626
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "text-xs font-medium text-muted-foreground", children: "Source messages" }),
2627
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "mt-2 flex flex-wrap gap-1", children: node.sourceMessageIds.length ? node.sourceMessageIds.map((id) => /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(Badge, { className: "max-w-full truncate", variant: "outline", children: id }, id)) : /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "text-xs text-muted-foreground", children: "-" }) })
2628
+ ] })
2629
+ ] });
2630
+ }
2631
+ function MatchBadges({ match }) {
2632
+ if (!match?.reasons.length) return null;
2633
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "mt-2 flex flex-wrap gap-1", children: match.reasons.slice(0, 4).map((reason) => /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(Badge, { className: "text-[10px]", variant: "outline", children: reason }, reason)) });
2634
+ }
2446
2635
  function InspectorRow({
2447
2636
  label,
2448
2637
  value
@@ -2471,6 +2660,12 @@ function formatDateTime(value) {
2471
2660
  if (Number.isNaN(date.getTime())) return value;
2472
2661
  return date.toLocaleString();
2473
2662
  }
2663
+ function formatNullableNumber(value) {
2664
+ return typeof value === "number" && Number.isFinite(value) ? value.toFixed(2) : "-";
2665
+ }
2666
+ function formatSimilarityScore(value) {
2667
+ return Number.isFinite(value) ? `Similarity ${value.toFixed(2)}` : "-";
2668
+ }
2474
2669
  function emptyBrainResponse() {
2475
2670
  return {
2476
2671
  nodes: [],
@@ -2482,6 +2677,14 @@ function emptyBrainResponse() {
2482
2677
  byKind: {},
2483
2678
  byStatus: {}
2484
2679
  },
2680
+ matches: {},
2681
+ related: [],
2682
+ similar: [],
2683
+ semantic: {
2684
+ requested: false,
2685
+ available: false,
2686
+ error: null
2687
+ },
2485
2688
  pageInfo: {
2486
2689
  limit: 0,
2487
2690
  offset: 0,