@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.d.cts CHANGED
@@ -139,6 +139,7 @@ interface AdminEventFilters {
139
139
  }
140
140
  type AdminBrainLayer = "all" | "knowledge" | "working" | (string & {});
141
141
  type AdminBrainStatus = "all" | "active" | "superseded" | "archived" | (string & {});
142
+ type AdminBrainSearchMode = "keyword" | "semantic" | "hybrid";
142
143
  interface AdminBrainFilters {
143
144
  namespace?: string;
144
145
  memorySpaceId?: string;
@@ -149,6 +150,14 @@ interface AdminBrainFilters {
149
150
  kind?: string;
150
151
  status?: AdminBrainStatus;
151
152
  search?: string;
153
+ searchMode?: AdminBrainSearchMode;
154
+ focusNodeId?: string;
155
+ includeRelated?: boolean;
156
+ includeSimilar?: boolean;
157
+ similarLimit?: number;
158
+ minSimilarity?: number;
159
+ relationDepth?: 1;
160
+ relationTypes?: string[];
152
161
  limit?: number;
153
162
  offset?: number;
154
163
  }
@@ -176,6 +185,12 @@ interface AdminBrainNode {
176
185
  y: number;
177
186
  data: Record<string, unknown>;
178
187
  }
188
+ interface AdminBrainMatch {
189
+ keyword?: boolean;
190
+ similarity?: number;
191
+ relationDistance?: number;
192
+ reasons: string[];
193
+ }
179
194
  interface AdminBrainEdge {
180
195
  id: string;
181
196
  sourceNodeId: string;
@@ -200,11 +215,28 @@ interface AdminBrainStats {
200
215
  byKind: Record<string, number>;
201
216
  byStatus: Record<string, number>;
202
217
  }
218
+ interface AdminBrainRelated {
219
+ node: AdminBrainNode;
220
+ edge: AdminBrainEdge;
221
+ direction: "in" | "out";
222
+ }
223
+ interface AdminBrainSimilar {
224
+ node: AdminBrainNode;
225
+ similarity: number;
226
+ }
203
227
  interface AdminBrainResponse {
204
228
  nodes: AdminBrainNode[];
205
229
  edges: AdminBrainEdge[];
206
230
  clusters: AdminBrainCluster[];
207
231
  stats: AdminBrainStats;
232
+ matches?: Record<string, AdminBrainMatch>;
233
+ related?: AdminBrainRelated[];
234
+ similar?: AdminBrainSimilar[];
235
+ semantic?: {
236
+ requested: boolean;
237
+ available: boolean;
238
+ error: string | null;
239
+ };
208
240
  pageInfo: {
209
241
  limit: number;
210
242
  offset: number;
@@ -621,4 +653,4 @@ declare function StatusBadge({ status }: {
621
653
  status?: string | null;
622
654
  }): react_jsx_runtime.JSX.Element;
623
655
 
624
- export { ADMIN_GROUP_LABELS, ADMIN_GROUP_ORDER, type AdminActivityInterval, type AdminActivityPoint, type AdminAgentSummary, type AdminBrainCluster, type AdminBrainEdge, type AdminBrainFilters, type AdminBrainLayer, type AdminBrainNode, type AdminBrainResponse, type AdminBrainStats, type AdminBrainStatus, type AdminBranding, type AdminClientConfig, type AdminClientOptions, type AdminClientPaths, type AdminCollectionItem, type AdminCollectionPage, type AdminDatePreset, type AdminDetailPanel, type AdminEventFilters, type AdminMessage, type AdminMessagePage, type AdminMessagePageInfo, type AdminModule, type AdminModuleGroup, type AdminNavItem, type AdminOverview, type AdminParticipantDetail, type AdminParticipantSummary, type AdminPermissions, type AdminQueueEvent, type AdminRouteDefinition, type AdminRouteState, type AdminRuntimeContext, type AdminScope, type AdminThreadDetail, type AdminThreadSummary, type AdminUsageAttribution, type AdminUsageBreakdown, type AdminUsageDimension, type AdminUsageFilters, type AdminUsageGroupBy, type AdminUsageInterval, type AdminUsageKind, type AdminUsageMetricKind, type AdminUsagePoint, type AdminUsageResponse, type AdminUsageTotals, type AggregatedUsageRow, type CollectionEditor, type CollectionEditorProps, CopilotzAdmin, type CopilotzAdminClient, type CopilotzAdminProps, EMPTY_USAGE_TOTALS, EmptyState, FilterBar, InspectorPanel, JsonPanel, type LegacyAdminConfig, MetricStrip, type MetricStripItem, PageHeader, type RequestHeadersProvider, ResourceTable, type ResourceTableColumn, StatusBadge, type UsageCalculationOptions, type UsageChartSeries, type UsageChartState, type UseCopilotzAdminOptions, type UseCopilotzAdminResult, addUsageTotals, agentsModule, aggregateUsageRows, brainModule, buildUsageChartState, canAccessAdminPermission, collectAdminNavItems, collectAdminRoutes, collectCollectionEditors, collectionsModule, createAdminClient, defaultAdminConfig, defaultCopilotzModules, eventsModule, firstAccessibleRoute, formatCompactMetric, formatMetricValue, formatNumber, formatPercent, formatUsageBucket, getUsageDimensionLabel, getUsageGroupLabel, getUsageMetricLabel, getUsageRange, getUsageTotalValue, mergeAdminConfig, overviewModule, participantsModule, threadsModule, usageModule, useAdmin, useCopilotzAdmin };
656
+ export { ADMIN_GROUP_LABELS, ADMIN_GROUP_ORDER, type AdminActivityInterval, type AdminActivityPoint, type AdminAgentSummary, type AdminBrainCluster, type AdminBrainEdge, type AdminBrainFilters, type AdminBrainLayer, type AdminBrainMatch, type AdminBrainNode, type AdminBrainRelated, type AdminBrainResponse, type AdminBrainSearchMode, type AdminBrainSimilar, type AdminBrainStats, type AdminBrainStatus, type AdminBranding, type AdminClientConfig, type AdminClientOptions, type AdminClientPaths, type AdminCollectionItem, type AdminCollectionPage, type AdminDatePreset, type AdminDetailPanel, type AdminEventFilters, type AdminMessage, type AdminMessagePage, type AdminMessagePageInfo, type AdminModule, type AdminModuleGroup, type AdminNavItem, type AdminOverview, type AdminParticipantDetail, type AdminParticipantSummary, type AdminPermissions, type AdminQueueEvent, type AdminRouteDefinition, type AdminRouteState, type AdminRuntimeContext, type AdminScope, type AdminThreadDetail, type AdminThreadSummary, type AdminUsageAttribution, type AdminUsageBreakdown, type AdminUsageDimension, type AdminUsageFilters, type AdminUsageGroupBy, type AdminUsageInterval, type AdminUsageKind, type AdminUsageMetricKind, type AdminUsagePoint, type AdminUsageResponse, type AdminUsageTotals, type AggregatedUsageRow, type CollectionEditor, type CollectionEditorProps, CopilotzAdmin, type CopilotzAdminClient, type CopilotzAdminProps, EMPTY_USAGE_TOTALS, EmptyState, FilterBar, InspectorPanel, JsonPanel, type LegacyAdminConfig, MetricStrip, type MetricStripItem, PageHeader, type RequestHeadersProvider, ResourceTable, type ResourceTableColumn, StatusBadge, type UsageCalculationOptions, type UsageChartSeries, type UsageChartState, type UseCopilotzAdminOptions, type UseCopilotzAdminResult, addUsageTotals, agentsModule, aggregateUsageRows, brainModule, buildUsageChartState, canAccessAdminPermission, collectAdminNavItems, collectAdminRoutes, collectCollectionEditors, collectionsModule, createAdminClient, defaultAdminConfig, defaultCopilotzModules, eventsModule, firstAccessibleRoute, formatCompactMetric, formatMetricValue, formatNumber, formatPercent, formatUsageBucket, getUsageDimensionLabel, getUsageGroupLabel, getUsageMetricLabel, getUsageRange, getUsageTotalValue, mergeAdminConfig, overviewModule, participantsModule, threadsModule, usageModule, useAdmin, useCopilotzAdmin };
package/dist/index.d.ts CHANGED
@@ -139,6 +139,7 @@ interface AdminEventFilters {
139
139
  }
140
140
  type AdminBrainLayer = "all" | "knowledge" | "working" | (string & {});
141
141
  type AdminBrainStatus = "all" | "active" | "superseded" | "archived" | (string & {});
142
+ type AdminBrainSearchMode = "keyword" | "semantic" | "hybrid";
142
143
  interface AdminBrainFilters {
143
144
  namespace?: string;
144
145
  memorySpaceId?: string;
@@ -149,6 +150,14 @@ interface AdminBrainFilters {
149
150
  kind?: string;
150
151
  status?: AdminBrainStatus;
151
152
  search?: string;
153
+ searchMode?: AdminBrainSearchMode;
154
+ focusNodeId?: string;
155
+ includeRelated?: boolean;
156
+ includeSimilar?: boolean;
157
+ similarLimit?: number;
158
+ minSimilarity?: number;
159
+ relationDepth?: 1;
160
+ relationTypes?: string[];
152
161
  limit?: number;
153
162
  offset?: number;
154
163
  }
@@ -176,6 +185,12 @@ interface AdminBrainNode {
176
185
  y: number;
177
186
  data: Record<string, unknown>;
178
187
  }
188
+ interface AdminBrainMatch {
189
+ keyword?: boolean;
190
+ similarity?: number;
191
+ relationDistance?: number;
192
+ reasons: string[];
193
+ }
179
194
  interface AdminBrainEdge {
180
195
  id: string;
181
196
  sourceNodeId: string;
@@ -200,11 +215,28 @@ interface AdminBrainStats {
200
215
  byKind: Record<string, number>;
201
216
  byStatus: Record<string, number>;
202
217
  }
218
+ interface AdminBrainRelated {
219
+ node: AdminBrainNode;
220
+ edge: AdminBrainEdge;
221
+ direction: "in" | "out";
222
+ }
223
+ interface AdminBrainSimilar {
224
+ node: AdminBrainNode;
225
+ similarity: number;
226
+ }
203
227
  interface AdminBrainResponse {
204
228
  nodes: AdminBrainNode[];
205
229
  edges: AdminBrainEdge[];
206
230
  clusters: AdminBrainCluster[];
207
231
  stats: AdminBrainStats;
232
+ matches?: Record<string, AdminBrainMatch>;
233
+ related?: AdminBrainRelated[];
234
+ similar?: AdminBrainSimilar[];
235
+ semantic?: {
236
+ requested: boolean;
237
+ available: boolean;
238
+ error: string | null;
239
+ };
208
240
  pageInfo: {
209
241
  limit: number;
210
242
  offset: number;
@@ -621,4 +653,4 @@ declare function StatusBadge({ status }: {
621
653
  status?: string | null;
622
654
  }): react_jsx_runtime.JSX.Element;
623
655
 
624
- export { ADMIN_GROUP_LABELS, ADMIN_GROUP_ORDER, type AdminActivityInterval, type AdminActivityPoint, type AdminAgentSummary, type AdminBrainCluster, type AdminBrainEdge, type AdminBrainFilters, type AdminBrainLayer, type AdminBrainNode, type AdminBrainResponse, type AdminBrainStats, type AdminBrainStatus, type AdminBranding, type AdminClientConfig, type AdminClientOptions, type AdminClientPaths, type AdminCollectionItem, type AdminCollectionPage, type AdminDatePreset, type AdminDetailPanel, type AdminEventFilters, type AdminMessage, type AdminMessagePage, type AdminMessagePageInfo, type AdminModule, type AdminModuleGroup, type AdminNavItem, type AdminOverview, type AdminParticipantDetail, type AdminParticipantSummary, type AdminPermissions, type AdminQueueEvent, type AdminRouteDefinition, type AdminRouteState, type AdminRuntimeContext, type AdminScope, type AdminThreadDetail, type AdminThreadSummary, type AdminUsageAttribution, type AdminUsageBreakdown, type AdminUsageDimension, type AdminUsageFilters, type AdminUsageGroupBy, type AdminUsageInterval, type AdminUsageKind, type AdminUsageMetricKind, type AdminUsagePoint, type AdminUsageResponse, type AdminUsageTotals, type AggregatedUsageRow, type CollectionEditor, type CollectionEditorProps, CopilotzAdmin, type CopilotzAdminClient, type CopilotzAdminProps, EMPTY_USAGE_TOTALS, EmptyState, FilterBar, InspectorPanel, JsonPanel, type LegacyAdminConfig, MetricStrip, type MetricStripItem, PageHeader, type RequestHeadersProvider, ResourceTable, type ResourceTableColumn, StatusBadge, type UsageCalculationOptions, type UsageChartSeries, type UsageChartState, type UseCopilotzAdminOptions, type UseCopilotzAdminResult, addUsageTotals, agentsModule, aggregateUsageRows, brainModule, buildUsageChartState, canAccessAdminPermission, collectAdminNavItems, collectAdminRoutes, collectCollectionEditors, collectionsModule, createAdminClient, defaultAdminConfig, defaultCopilotzModules, eventsModule, firstAccessibleRoute, formatCompactMetric, formatMetricValue, formatNumber, formatPercent, formatUsageBucket, getUsageDimensionLabel, getUsageGroupLabel, getUsageMetricLabel, getUsageRange, getUsageTotalValue, mergeAdminConfig, overviewModule, participantsModule, threadsModule, usageModule, useAdmin, useCopilotzAdmin };
656
+ export { ADMIN_GROUP_LABELS, ADMIN_GROUP_ORDER, type AdminActivityInterval, type AdminActivityPoint, type AdminAgentSummary, type AdminBrainCluster, type AdminBrainEdge, type AdminBrainFilters, type AdminBrainLayer, type AdminBrainMatch, type AdminBrainNode, type AdminBrainRelated, type AdminBrainResponse, type AdminBrainSearchMode, type AdminBrainSimilar, type AdminBrainStats, type AdminBrainStatus, type AdminBranding, type AdminClientConfig, type AdminClientOptions, type AdminClientPaths, type AdminCollectionItem, type AdminCollectionPage, type AdminDatePreset, type AdminDetailPanel, type AdminEventFilters, type AdminMessage, type AdminMessagePage, type AdminMessagePageInfo, type AdminModule, type AdminModuleGroup, type AdminNavItem, type AdminOverview, type AdminParticipantDetail, type AdminParticipantSummary, type AdminPermissions, type AdminQueueEvent, type AdminRouteDefinition, type AdminRouteState, type AdminRuntimeContext, type AdminScope, type AdminThreadDetail, type AdminThreadSummary, type AdminUsageAttribution, type AdminUsageBreakdown, type AdminUsageDimension, type AdminUsageFilters, type AdminUsageGroupBy, type AdminUsageInterval, type AdminUsageKind, type AdminUsageMetricKind, type AdminUsagePoint, type AdminUsageResponse, type AdminUsageTotals, type AggregatedUsageRow, type CollectionEditor, type CollectionEditorProps, CopilotzAdmin, type CopilotzAdminClient, type CopilotzAdminProps, EMPTY_USAGE_TOTALS, EmptyState, FilterBar, InspectorPanel, JsonPanel, type LegacyAdminConfig, MetricStrip, type MetricStripItem, PageHeader, type RequestHeadersProvider, ResourceTable, type ResourceTableColumn, StatusBadge, type UsageCalculationOptions, type UsageChartSeries, type UsageChartState, type UseCopilotzAdminOptions, type UseCopilotzAdminResult, addUsageTotals, agentsModule, aggregateUsageRows, brainModule, buildUsageChartState, canAccessAdminPermission, collectAdminNavItems, collectAdminRoutes, collectCollectionEditors, collectionsModule, createAdminClient, defaultAdminConfig, defaultCopilotzModules, eventsModule, firstAccessibleRoute, formatCompactMetric, formatMetricValue, formatNumber, formatPercent, formatUsageBucket, getUsageDimensionLabel, getUsageGroupLabel, getUsageMetricLabel, getUsageRange, getUsageTotalValue, mergeAdminConfig, overviewModule, participantsModule, threadsModule, usageModule, useAdmin, useCopilotzAdmin };
package/dist/index.js CHANGED
@@ -234,6 +234,14 @@ function createAdminClient(options = {}) {
234
234
  kind: filters.kind === "all" ? void 0 : filters.kind,
235
235
  status: filters.status === "all" ? void 0 : filters.status,
236
236
  search: filters.search,
237
+ searchMode: filters.searchMode,
238
+ focusNodeId: filters.focusNodeId,
239
+ includeRelated: filters.includeRelated ? "true" : void 0,
240
+ includeSimilar: filters.includeSimilar ? "true" : void 0,
241
+ similarLimit: filters.similarLimit ? String(filters.similarLimit) : void 0,
242
+ minSimilarity: typeof filters.minSimilarity === "number" ? String(filters.minSimilarity) : void 0,
243
+ relationDepth: filters.relationDepth ? String(filters.relationDepth) : void 0,
244
+ relationTypes: filters.relationTypes?.join(","),
237
245
  limit: String(filters.limit ?? 160),
238
246
  offset: filters.offset ? String(filters.offset) : void 0
239
247
  }),
@@ -1950,6 +1958,7 @@ import React7 from "react";
1950
1958
  import {
1951
1959
  Brain as Brain2,
1952
1960
  CircleDot,
1961
+ GitBranch,
1953
1962
  Network,
1954
1963
  RefreshCw,
1955
1964
  Search as Search2,
@@ -1957,6 +1966,7 @@ import {
1957
1966
  } from "lucide-react";
1958
1967
  import { jsx as jsx21, jsxs as jsxs13 } from "react/jsx-runtime";
1959
1968
  var BRAIN_LAYERS = ["all", "knowledge", "working"];
1969
+ var BRAIN_SEARCH_MODES = ["hybrid", "semantic", "keyword"];
1960
1970
  var BRAIN_STATUSES = ["active", "all", "superseded", "archived"];
1961
1971
  var BRAIN_KINDS = [
1962
1972
  "all",
@@ -1994,6 +2004,9 @@ function brainModule() {
1994
2004
  }
1995
2005
  function BrainPage({ context }) {
1996
2006
  const [search, setSearch] = React7.useState("");
2007
+ const [searchMode, setSearchMode] = React7.useState(
2008
+ "hybrid"
2009
+ );
1997
2010
  const [layer, setLayer] = React7.useState("all");
1998
2011
  const [status, setStatus] = React7.useState(
1999
2012
  "active"
@@ -2006,24 +2019,32 @@ function BrainPage({ context }) {
2006
2019
  const [selectedNode, setSelectedNode] = React7.useState(
2007
2020
  null
2008
2021
  );
2022
+ const [focusNodeId, setFocusNodeId] = React7.useState(null);
2009
2023
  const [loading, setLoading] = React7.useState(false);
2010
2024
  const [error, setError] = React7.useState(null);
2011
- const loadBrain = React7.useCallback(async () => {
2025
+ const loadBrain = React7.useCallback(async (focusOverride) => {
2012
2026
  setLoading(true);
2013
2027
  setError(null);
2028
+ const effectiveFocusNodeId = focusOverride === void 0 ? focusNodeId : focusOverride;
2014
2029
  try {
2015
2030
  const next = await context.client.getBrain({
2016
2031
  namespace: context.scope.namespace || void 0,
2017
2032
  search: search.trim() || void 0,
2033
+ searchMode,
2018
2034
  layer,
2019
2035
  status,
2020
2036
  kind,
2021
2037
  agentId: agentId.trim() || void 0,
2038
+ focusNodeId: effectiveFocusNodeId || void 0,
2039
+ includeRelated: Boolean(effectiveFocusNodeId),
2040
+ includeSimilar: Boolean(effectiveFocusNodeId),
2041
+ similarLimit: 24,
2042
+ minSimilarity: 0.2,
2022
2043
  limit: 180
2023
2044
  });
2024
2045
  setResponse(next);
2025
2046
  setSelectedNode(
2026
- (current) => current && next.nodes.some((node) => node.id === current.id) ? current : next.nodes[0] ?? null
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
2027
2048
  );
2028
2049
  } catch (cause) {
2029
2050
  setError(cause instanceof Error ? cause.message : "Failed to load brain");
@@ -2036,17 +2057,30 @@ function BrainPage({ context }) {
2036
2057
  agentId,
2037
2058
  context.client,
2038
2059
  context.scope.namespace,
2060
+ focusNodeId,
2039
2061
  kind,
2040
2062
  layer,
2041
2063
  search,
2064
+ searchMode,
2042
2065
  status
2043
2066
  ]);
2044
2067
  React7.useEffect(() => {
2045
2068
  void loadBrain();
2046
- }, [context.refreshKey, context.scope.namespace, loadBrain]);
2069
+ }, [context.refreshKey, context.scope.namespace, focusNodeId, loadBrain]);
2047
2070
  const data = response ?? emptyBrainResponse();
2071
+ const matches = data.matches ?? {};
2072
+ const related = data.related ?? [];
2073
+ const similar = data.similar ?? [];
2048
2074
  const knowledgeCount = data.stats.byLayer.knowledge ?? 0;
2049
2075
  const workingCount = data.stats.byLayer.working ?? 0;
2076
+ const selectNode = React7.useCallback((node) => {
2077
+ setSelectedNode(node);
2078
+ setFocusNodeId(node.id);
2079
+ }, []);
2080
+ const runSearch = React7.useCallback(() => {
2081
+ setFocusNodeId(null);
2082
+ void loadBrain(null);
2083
+ }, [loadBrain]);
2050
2084
  return /* @__PURE__ */ jsxs13("div", { className: "space-y-4", children: [
2051
2085
  /* @__PURE__ */ jsx21(
2052
2086
  PageHeader,
@@ -2113,7 +2147,7 @@ function BrainPage({ context }) {
2113
2147
  Button,
2114
2148
  {
2115
2149
  disabled: loading,
2116
- onClick: () => void loadBrain(),
2150
+ onClick: runSearch,
2117
2151
  size: "sm",
2118
2152
  type: "button",
2119
2153
  children: [
@@ -2126,6 +2160,19 @@ function BrainPage({ context }) {
2126
2160
  searchPlaceholder: "Search brain",
2127
2161
  searchValue: search,
2128
2162
  children: [
2163
+ /* @__PURE__ */ jsx21("div", { className: "inline-flex h-8 overflow-hidden rounded-md border bg-background", children: BRAIN_SEARCH_MODES.map((mode) => /* @__PURE__ */ jsx21(
2164
+ "button",
2165
+ {
2166
+ className: cn(
2167
+ "px-3 text-xs transition-colors",
2168
+ searchMode === mode ? "bg-primary text-primary-foreground" : "text-muted-foreground hover:bg-muted"
2169
+ ),
2170
+ onClick: () => setSearchMode(mode),
2171
+ type: "button",
2172
+ children: formatLabel(mode)
2173
+ },
2174
+ mode
2175
+ )) }),
2129
2176
  /* @__PURE__ */ jsxs13(
2130
2177
  Select,
2131
2178
  {
@@ -2165,7 +2212,7 @@ function BrainPage({ context }) {
2165
2212
  className: "h-8 w-[190px]",
2166
2213
  onChange: (event) => setAgentId(event.target.value),
2167
2214
  onKeyDown: (event) => {
2168
- if (event.key === "Enter") void loadBrain();
2215
+ if (event.key === "Enter") runSearch();
2169
2216
  },
2170
2217
  placeholder: "Agent ID",
2171
2218
  value: agentId
@@ -2174,10 +2221,23 @@ function BrainPage({ context }) {
2174
2221
  ]
2175
2222
  }
2176
2223
  ),
2224
+ data.semantic?.requested && data.semantic.error ? /* @__PURE__ */ jsxs13("div", { className: "rounded-md border border-destructive/30 bg-destructive/5 px-3 py-2 text-xs text-destructive", children: [
2225
+ "Semantic search unavailable: ",
2226
+ data.semantic.error
2227
+ ] }) : null,
2177
2228
  error ? /* @__PURE__ */ jsx21(EmptyState, { title: "Unable to load brain", description: error }) : /* @__PURE__ */ jsx21(
2178
2229
  InspectorPanel,
2179
2230
  {
2180
- side: selectedNode ? /* @__PURE__ */ jsx21(BrainNodeInspector, { node: selectedNode }) : /* @__PURE__ */ jsx21(
2231
+ side: selectedNode ? /* @__PURE__ */ jsx21(
2232
+ BrainNodeInspector,
2233
+ {
2234
+ match: matches[selectedNode.id],
2235
+ node: selectedNode,
2236
+ onSelectNode: selectNode,
2237
+ related,
2238
+ similar
2239
+ }
2240
+ ) : /* @__PURE__ */ jsx21(
2181
2241
  EmptyState,
2182
2242
  {
2183
2243
  icon: Brain2,
@@ -2193,7 +2253,8 @@ function BrainPage({ context }) {
2193
2253
  edges: data.edges,
2194
2254
  loading,
2195
2255
  nodes: data.nodes,
2196
- onSelectNode: setSelectedNode,
2256
+ onSelectNode: selectNode,
2257
+ similar,
2197
2258
  selectedNodeId: selectedNode?.id ?? null
2198
2259
  }
2199
2260
  ),
@@ -2202,7 +2263,7 @@ function BrainPage({ context }) {
2202
2263
  {
2203
2264
  rows: data.nodes,
2204
2265
  getRowKey: (row) => row.id,
2205
- onRowClick: setSelectedNode,
2266
+ onRowClick: selectNode,
2206
2267
  empty: /* @__PURE__ */ jsx21(
2207
2268
  EmptyState,
2208
2269
  {
@@ -2218,7 +2279,8 @@ function BrainPage({ context }) {
2218
2279
  className: "max-w-[360px]",
2219
2280
  render: (row) => /* @__PURE__ */ jsxs13("div", { className: "min-w-0", children: [
2220
2281
  /* @__PURE__ */ jsx21("div", { className: "truncate font-medium", children: row.name }),
2221
- /* @__PURE__ */ jsx21("div", { className: "truncate text-xs text-muted-foreground", children: row.content || "-" })
2282
+ /* @__PURE__ */ jsx21("div", { className: "truncate text-xs text-muted-foreground", children: row.content || "-" }),
2283
+ /* @__PURE__ */ jsx21(MatchBadges, { match: matches[row.id] })
2222
2284
  ] })
2223
2285
  },
2224
2286
  {
@@ -2256,6 +2318,7 @@ function BrainMap({
2256
2318
  loading,
2257
2319
  nodes,
2258
2320
  onSelectNode,
2321
+ similar,
2259
2322
  selectedNodeId
2260
2323
  }) {
2261
2324
  const nodeById = React7.useMemo(
@@ -2308,12 +2371,15 @@ function BrainMap({
2308
2371
  const source = nodeById.get(edge.sourceNodeId);
2309
2372
  const target = nodeById.get(edge.targetNodeId);
2310
2373
  if (!source || !target) return null;
2374
+ const isSelectedEdge = Boolean(
2375
+ selectedNodeId && (edge.sourceNodeId === selectedNodeId || edge.targetNodeId === selectedNodeId)
2376
+ );
2311
2377
  return /* @__PURE__ */ jsx21(
2312
2378
  "line",
2313
2379
  {
2314
2380
  stroke: edge.type === "contradicts" ? "#dc2626" : "#64748b",
2315
- strokeOpacity: "0.32",
2316
- strokeWidth: edge.type === "supports" ? 2 : 1.3,
2381
+ strokeOpacity: isSelectedEdge ? "0.75" : "0.32",
2382
+ strokeWidth: isSelectedEdge ? 2.6 : edge.type === "supports" ? 2 : 1.3,
2317
2383
  x1: source.x * 1e3,
2318
2384
  x2: target.x * 1e3,
2319
2385
  y1: source.y * 600,
@@ -2322,6 +2388,25 @@ function BrainMap({
2322
2388
  edge.id
2323
2389
  );
2324
2390
  }),
2391
+ selectedNodeId ? similar.map((item) => {
2392
+ const source = nodeById.get(selectedNodeId);
2393
+ const target = nodeById.get(item.node.id);
2394
+ if (!source || !target) return null;
2395
+ return /* @__PURE__ */ jsx21(
2396
+ "line",
2397
+ {
2398
+ stroke: "#0ea5e9",
2399
+ strokeDasharray: "5 6",
2400
+ strokeOpacity: "0.42",
2401
+ strokeWidth: "1.8",
2402
+ x1: source.x * 1e3,
2403
+ x2: target.x * 1e3,
2404
+ y1: source.y * 600,
2405
+ y2: target.y * 600
2406
+ },
2407
+ `similar-${item.node.id}`
2408
+ );
2409
+ }) : null,
2325
2410
  nodes.map((node) => {
2326
2411
  const isSelected = node.id === selectedNodeId;
2327
2412
  return /* @__PURE__ */ jsx21(
@@ -2349,7 +2434,14 @@ function BrainMap({
2349
2434
  }
2350
2435
  ) });
2351
2436
  }
2352
- function BrainNodeInspector({ node }) {
2437
+ function BrainNodeInspector({
2438
+ match,
2439
+ node,
2440
+ onSelectNode,
2441
+ related,
2442
+ similar
2443
+ }) {
2444
+ const [tab, setTab] = React7.useState("overview");
2353
2445
  return /* @__PURE__ */ jsxs13("div", { className: "space-y-3", children: [
2354
2446
  /* @__PURE__ */ jsxs13("div", { className: "rounded-lg border bg-background p-4", children: [
2355
2447
  /* @__PURE__ */ jsxs13("div", { className: "flex flex-wrap items-center gap-2", children: [
@@ -2359,24 +2451,122 @@ function BrainNodeInspector({ node }) {
2359
2451
  ] }),
2360
2452
  /* @__PURE__ */ jsx21("h3", { className: "mt-3 text-base font-semibold", children: node.name }),
2361
2453
  /* @__PURE__ */ jsx21("p", { className: "mt-2 text-sm leading-6 text-muted-foreground", children: node.content || "-" }),
2362
- /* @__PURE__ */ jsxs13("dl", { className: "mt-4 grid gap-2 text-xs", children: [
2363
- /* @__PURE__ */ jsx21(InspectorRow, { label: "Agent", value: node.agentId }),
2364
- /* @__PURE__ */ jsx21(InspectorRow, { label: "Thread", value: node.threadId }),
2365
- /* @__PURE__ */ jsx21(InspectorRow, { label: "Memory space", value: node.memorySpaceId }),
2366
- /* @__PURE__ */ jsx21(InspectorRow, { label: "Checkpoint", value: node.checkpointId }),
2367
- /* @__PURE__ */ jsx21(InspectorRow, { label: "Source field", value: node.sourceField }),
2368
- /* @__PURE__ */ jsx21(
2369
- InspectorRow,
2370
- {
2371
- label: "Updated",
2372
- value: formatDateTime(node.updatedAt)
2373
- }
2374
- )
2375
- ] })
2454
+ /* @__PURE__ */ jsx21(MatchBadges, { match })
2376
2455
  ] }),
2377
- /* @__PURE__ */ jsx21(JsonPanel, { title: "Node JSON", value: node, minHeight: 300 })
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(
2457
+ "button",
2458
+ {
2459
+ className: cn(
2460
+ "min-w-0 flex-1 px-2 py-2 text-xs transition-colors",
2461
+ tab === item ? "bg-muted font-medium text-foreground" : "text-muted-foreground hover:bg-muted/60"
2462
+ ),
2463
+ onClick: () => setTab(item),
2464
+ type: "button",
2465
+ children: formatLabel(item)
2466
+ },
2467
+ item
2468
+ )) }),
2469
+ tab === "overview" ? /* @__PURE__ */ jsx21(BrainOverview, { node }) : null,
2470
+ tab === "related" ? /* @__PURE__ */ jsx21(RelatedPanel, { onSelectNode, related }) : null,
2471
+ tab === "similar" ? /* @__PURE__ */ jsx21(SimilarPanel, { onSelectNode, similar }) : null,
2472
+ tab === "evidence" ? /* @__PURE__ */ jsx21(EvidencePanel, { node }) : null,
2473
+ tab === "json" ? /* @__PURE__ */ jsx21(JsonPanel, { title: "Node JSON", value: node, minHeight: 300 }) : null
2378
2474
  ] });
2379
2475
  }
2476
+ function BrainOverview({ node }) {
2477
+ return /* @__PURE__ */ jsx21("div", { className: "rounded-lg border bg-background p-4", children: /* @__PURE__ */ jsxs13("dl", { className: "grid gap-2 text-xs", children: [
2478
+ /* @__PURE__ */ jsx21(InspectorRow, { label: "Agent", value: node.agentId }),
2479
+ /* @__PURE__ */ jsx21(InspectorRow, { label: "Thread", value: node.threadId }),
2480
+ /* @__PURE__ */ jsx21(InspectorRow, { label: "Memory space", value: node.memorySpaceId }),
2481
+ /* @__PURE__ */ jsx21(InspectorRow, { label: "Checkpoint", value: node.checkpointId }),
2482
+ /* @__PURE__ */ jsx21(InspectorRow, { label: "Source field", value: node.sourceField }),
2483
+ /* @__PURE__ */ jsx21(InspectorRow, { label: "Confidence", value: formatNullableNumber(node.confidence) }),
2484
+ /* @__PURE__ */ jsx21(InspectorRow, { label: "Updated", value: formatDateTime(node.updatedAt) })
2485
+ ] }) });
2486
+ }
2487
+ function RelatedPanel({
2488
+ onSelectNode,
2489
+ related
2490
+ }) {
2491
+ if (related.length === 0) {
2492
+ return /* @__PURE__ */ jsx21(
2493
+ EmptyState,
2494
+ {
2495
+ icon: GitBranch,
2496
+ title: "No explicit relations",
2497
+ description: "Relation edges will appear here when this node is connected to other concepts."
2498
+ }
2499
+ );
2500
+ }
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
+ )) });
2518
+ }
2519
+ function SimilarPanel({
2520
+ onSelectNode,
2521
+ similar
2522
+ }) {
2523
+ if (similar.length === 0) {
2524
+ return /* @__PURE__ */ jsx21(
2525
+ EmptyState,
2526
+ {
2527
+ icon: Sparkles,
2528
+ title: "No semantic neighbors",
2529
+ description: "Similar nodes appear here when the selected node has an embedding."
2530
+ }
2531
+ );
2532
+ }
2533
+ return /* @__PURE__ */ jsx21("div", { className: "space-y-2", children: similar.map((item) => /* @__PURE__ */ jsxs13(
2534
+ "button",
2535
+ {
2536
+ className: "w-full rounded-lg border bg-background p-3 text-left transition-colors hover:bg-muted/40",
2537
+ onClick: () => onSelectNode(item.node),
2538
+ type: "button",
2539
+ children: [
2540
+ /* @__PURE__ */ jsxs13("div", { className: "flex flex-wrap items-center gap-2", children: [
2541
+ /* @__PURE__ */ jsx21(Badge, { variant: "secondary", children: formatSimilarityScore(item.similarity) }),
2542
+ /* @__PURE__ */ jsx21(LayerBadge, { layer: item.node.layer }),
2543
+ /* @__PURE__ */ jsx21(Badge, { variant: "outline", children: formatLabel(item.node.kind) })
2544
+ ] }),
2545
+ /* @__PURE__ */ jsx21("div", { className: "mt-2 text-sm font-medium", children: item.node.name }),
2546
+ /* @__PURE__ */ jsx21("p", { className: "mt-1 line-clamp-3 text-xs leading-5 text-muted-foreground", children: item.node.content || "-" })
2547
+ ]
2548
+ },
2549
+ item.node.id
2550
+ )) });
2551
+ }
2552
+ function EvidencePanel({ node }) {
2553
+ return /* @__PURE__ */ jsxs13("div", { className: "rounded-lg border bg-background p-4", children: [
2554
+ /* @__PURE__ */ jsxs13("dl", { className: "grid gap-2 text-xs", children: [
2555
+ /* @__PURE__ */ jsx21(InspectorRow, { label: "Source", value: node.sourceType }),
2556
+ /* @__PURE__ */ jsx21(InspectorRow, { label: "Source ID", value: node.sourceId }),
2557
+ /* @__PURE__ */ jsx21(InspectorRow, { label: "Thread", value: node.threadId }),
2558
+ /* @__PURE__ */ jsx21(InspectorRow, { label: "Checkpoint", value: node.checkpointId })
2559
+ ] }),
2560
+ /* @__PURE__ */ jsxs13("div", { className: "mt-4", children: [
2561
+ /* @__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: "-" }) })
2563
+ ] })
2564
+ ] });
2565
+ }
2566
+ function MatchBadges({ match }) {
2567
+ if (!match?.reasons.length) return null;
2568
+ return /* @__PURE__ */ jsx21("div", { className: "mt-2 flex flex-wrap gap-1", children: match.reasons.slice(0, 4).map((reason) => /* @__PURE__ */ jsx21(Badge, { className: "text-[10px]", variant: "outline", children: reason }, reason)) });
2569
+ }
2380
2570
  function InspectorRow({
2381
2571
  label,
2382
2572
  value
@@ -2405,6 +2595,12 @@ function formatDateTime(value) {
2405
2595
  if (Number.isNaN(date.getTime())) return value;
2406
2596
  return date.toLocaleString();
2407
2597
  }
2598
+ function formatNullableNumber(value) {
2599
+ return typeof value === "number" && Number.isFinite(value) ? value.toFixed(2) : "-";
2600
+ }
2601
+ function formatSimilarityScore(value) {
2602
+ return Number.isFinite(value) ? `Similarity ${value.toFixed(2)}` : "-";
2603
+ }
2408
2604
  function emptyBrainResponse() {
2409
2605
  return {
2410
2606
  nodes: [],
@@ -2416,6 +2612,14 @@ function emptyBrainResponse() {
2416
2612
  byKind: {},
2417
2613
  byStatus: {}
2418
2614
  },
2615
+ matches: {},
2616
+ related: [],
2617
+ similar: [],
2618
+ semantic: {
2619
+ requested: false,
2620
+ available: false,
2621
+ error: null
2622
+ },
2419
2623
  pageInfo: {
2420
2624
  limit: 0,
2421
2625
  offset: 0,