Files
fusion/packages/dashboard/app/api/system/knowledge-graph.ts
gsxdsm dad726c777 FN-9038: add Knowledge Graph tab to Memory view
Expose knowledge graph data in the Memory view through a localized, resilient dashboard panel.

- Add protected knowledge graph API routes and access helpers.
- Add a Memory view tab, graph panel, loading/error states, and localized labels.
- Document graph navigation and cover access, route, hook, and component behavior.

Files changed:
 .changeset/fn-9038-knowledge-graph-tab.md          |   7 +
 docs/dashboard-guide.md                            |   2 +
 docs/knowledge-graph.md                            |  10 ++
 packages/dashboard/app/api/legacy.ts               |  17 ++-
 packages/dashboard/app/api/system/index.ts         |   1 +
 .../dashboard/app/api/system/knowledge-graph.ts    |  23 +++
 .../app/components/KnowledgeGraphPanel.css         |  35 +++++
 .../app/components/KnowledgeGraphPanel.tsx         |  68 +++++++++
 packages/dashboard/app/components/MemoryView.tsx   |  15 +-
 .../__tests__/KnowledgeGraphPanel.test.tsx         |  49 +++++++
 .../app/components/__tests__/MemoryView.test.tsx   |  13 ++
 .../app/hooks/__tests__/useKnowledgeGraph.test.ts  |  66 +++++++++
 packages/dashboard/app/hooks/useKnowledgeGraph.ts  |  94 ++++++++++++
 .../src/__tests__/knowledge-graph-access.test.ts   |  88 ++++++++++++
 packages/dashboard/src/knowledge-graph-access.ts   | 160 +++++++++++++++++++++
 .../register-knowledge-graph-routes.test.ts        |  75 ++++++++++
 .../src/routes/register-knowledge-routes.ts        | 120 ++++++++++++++++
 packages/i18n/locales/en/app.json                  |  15 +-
 packages/i18n/locales/es/app.json                  |  15 +-
 packages/i18n/locales/fr/app.json                  |  15 +-
 packages/i18n/locales/ko/app.json                  |  15 +-
 packages/i18n/locales/pt-BR/app.json               |  15 +-
 packages/i18n/locales/zh-CN/app.json               |  15 +-
 packages/i18n/locales/zh-TW/app.json               |  15 +-
 24 files changed, 939 insertions(+), 9 deletions(-)

Fusion-Task-Id: FN-9038

Fusion-Task-Lineage: 4200e214-ad22-4f92-b980-c9bcb8ffd3b9

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-08-13 16:48:01 -07:00

24 lines
3.9 KiB
TypeScript

import { api } from "../client/client.js";
import { withProjectId } from "../client/health.js";
export type KnowledgeGraphNode = { id: string; kind: "file" | "module" | "symbol" | "doc-concept" | "rationale"; name: string; owner: "file" | "derived"; ownerPath: string; source: { path: string; line: number; column: number }; attributes: Record<string, string> };
export type KnowledgeGraphEdge = { id: string; kind: "contains" | "imports" | "re-exports" | "relates-to" | "rationale-supports"; from: string; to: string; provenance: "extracted" | "inferred"; owner: "file" | "derived"; ownerPath: string; source: { path: string; line: number; column: number }; attributes: Record<string, string> };
export type KnowledgeGraphNeighbor = { node: KnowledgeGraphNode; distance: number; edges: KnowledgeGraphEdge[] };
export type KnowledgeGraphNodeQuery = { kinds?: string[]; pathPrefix?: string; idPrefix?: string; namePattern?: string; fnxcArea?: string; symbolKind?: string; owner?: string; limit?: number; offset?: number };
export type KnowledgeGraphStatus = { available: boolean; recoveryReason?: string | null; graphDir?: string; nodeCount?: number; edgeCount?: number; nodeKindCounts?: Record<string, number>; edgeKindCounts?: Record<string, number>; provenanceCounts?: Record<string, number>; ownerCounts?: Record<string, number>; fnxcAreas?: string[]; pathLimits: { defaultMaxHops: number; maxHops: number; maxExpansions: number } };
export type KnowledgeGraphPathResult =
| { outcome: "found"; path: { nodes: KnowledgeGraphNode[]; edges: KnowledgeGraphEdge[] }; hops: number; maxHops: number; expansions: number; truncated: false }
| { outcome: "not-found"; path: null; maxHops: number; expansions: number; truncated: false }
| { outcome: "limit-reached"; path: null; maxHops: number; expansions: number; truncated: true; limit: "max-hops" | "max-expansions" };
function query(params: Record<string, string | number | string[] | undefined>): string {
const entries = Object.entries(params).flatMap(([key, value]) => Array.isArray(value) ? value.filter(Boolean).map(item => [key, item] as const) : value === undefined || value === "" ? [] : [[key, String(value)] as const]);
return entries.length ? `?${entries.map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`).join("&")}` : "";
}
export function fetchKnowledgeGraphStatus(projectId?: string): Promise<KnowledgeGraphStatus> { return api(withProjectId("/knowledge/graph/status", projectId)); }
export function queryKnowledgeGraphNodes(projectId: string | undefined, filters: KnowledgeGraphNodeQuery = {}): Promise<{ nodes: KnowledgeGraphNode[]; total: number; limit: number; offset: number }> { return api(withProjectId(`/knowledge/graph/nodes${query(filters)}`, projectId)); }
export function fetchKnowledgeGraphNode(projectId: string | undefined, id: string): Promise<{ node: KnowledgeGraphNode; outgoing: KnowledgeGraphEdge[]; incoming: KnowledgeGraphEdge[]; outgoingTotal: number; incomingTotal: number }> { return api(withProjectId(`/knowledge/graph/node${query({ id })}`, projectId)); }
export function fetchKnowledgeGraphNeighbors(projectId: string | undefined, options: { nodeId: string; direction?: "out" | "in" | "both"; edgeKinds?: string[]; depth?: number; limit?: number }): Promise<{ neighbors: KnowledgeGraphNeighbor[]; total: number }> { return api(withProjectId(`/knowledge/graph/neighbors${query(options)}`, projectId)); }
export function fetchKnowledgeGraphPath(projectId: string | undefined, options: { fromId: string; toId: string; maxHops?: number }): Promise<KnowledgeGraphPathResult> { return api(withProjectId(`/knowledge/graph/path${query(options)}`, projectId)); }
export function buildKnowledgeGraphArtifacts(projectId?: string, force = false): Promise<{ changed: boolean; nodes: number; edges: number; stats: Record<string, unknown> }> { return api(withProjectId("/knowledge/graph/build", projectId), { method: "POST", body: JSON.stringify({ force }) }); }