@powerhousedao/knowledge-note 1.0.4 → 1.0.6

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.
Files changed (25) hide show
  1. package/dist/editors/knowledge-vault/components/ActivityView.d.ts +2 -0
  2. package/dist/editors/knowledge-vault/components/ActivityView.d.ts.map +1 -0
  3. package/dist/editors/knowledge-vault/components/ActivityView.js +188 -0
  4. package/dist/editors/knowledge-vault/components/DriveExplorer.d.ts.map +1 -1
  5. package/dist/editors/knowledge-vault/components/DriveExplorer.js +7 -1
  6. package/dist/editors/knowledge-vault/components/SearchView.d.ts.map +1 -1
  7. package/dist/editors/knowledge-vault/components/SearchView.js +18 -11
  8. package/dist/editors/knowledge-vault/hooks/use-graph-search.d.ts +2 -1
  9. package/dist/editors/knowledge-vault/hooks/use-graph-search.d.ts.map +1 -1
  10. package/dist/editors/knowledge-vault/hooks/use-graph-search.js +42 -2
  11. package/dist/package.json +1 -1
  12. package/dist/processors/graph-indexer/index.d.ts.map +1 -1
  13. package/dist/processors/graph-indexer/index.js +80 -0
  14. package/dist/processors/graph-indexer/migrations.d.ts.map +1 -1
  15. package/dist/processors/graph-indexer/migrations.js +46 -0
  16. package/dist/processors/graph-indexer/query.d.ts +29 -0
  17. package/dist/processors/graph-indexer/query.d.ts.map +1 -1
  18. package/dist/processors/graph-indexer/query.js +104 -0
  19. package/dist/processors/graph-indexer/schema.d.ts +13 -0
  20. package/dist/processors/graph-indexer/schema.d.ts.map +1 -1
  21. package/dist/style.css +7 -0
  22. package/dist/subgraphs/knowledge-graph/subgraph.d.ts +55 -0
  23. package/dist/subgraphs/knowledge-graph/subgraph.d.ts.map +1 -1
  24. package/dist/subgraphs/knowledge-graph/subgraph.js +72 -0
  25. package/package.json +1 -1
@@ -0,0 +1,2 @@
1
+ export declare function ActivityView(): import("react/jsx-runtime").JSX.Element;
2
+ //# sourceMappingURL=ActivityView.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ActivityView.d.ts","sourceRoot":"","sources":["../../../../editors/knowledge-vault/components/ActivityView.tsx"],"names":[],"mappings":"AAuHA,wBAAgB,YAAY,4CAkJ3B"}
@@ -0,0 +1,188 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useState, useEffect, useMemo, useCallback } from "react";
3
+ import { setSelectedNode, useSelectedDriveId, } from "@powerhousedao/reactor-browser";
4
+ const FILTER_TYPES = {
5
+ all: { label: "All", ops: [] },
6
+ content: {
7
+ label: "Content",
8
+ ops: ["SET_TITLE", "SET_DESCRIPTION", "SET_CONTENT", "SET_NOTE_TYPE"],
9
+ },
10
+ links: {
11
+ label: "Links",
12
+ ops: ["ADD_LINK", "REMOVE_LINK", "UPDATE_LINK_TYPE"],
13
+ },
14
+ lifecycle: {
15
+ label: "Lifecycle",
16
+ ops: [
17
+ "SET_STATUS",
18
+ "SUBMIT_FOR_REVIEW",
19
+ "APPROVE_NOTE",
20
+ "REJECT_NOTE",
21
+ "ARCHIVE_NOTE",
22
+ "RESTORE_NOTE",
23
+ ],
24
+ },
25
+ topics: { label: "Topics", ops: ["ADD_TOPIC", "REMOVE_TOPIC"] },
26
+ };
27
+ const OP_ICONS = {
28
+ SET_TITLE: "T",
29
+ SET_DESCRIPTION: "D",
30
+ SET_CONTENT: "C",
31
+ SET_NOTE_TYPE: "N",
32
+ ADD_LINK: "+L",
33
+ REMOVE_LINK: "-L",
34
+ ADD_TOPIC: "+#",
35
+ REMOVE_TOPIC: "-#",
36
+ SET_STATUS: "S",
37
+ SUBMIT_FOR_REVIEW: "R",
38
+ APPROVE_NOTE: "A",
39
+ REJECT_NOTE: "X",
40
+ ARCHIVE_NOTE: "Z",
41
+ RESTORE_NOTE: "U",
42
+ SET_PROVENANCE: "P",
43
+ SET_METADATA_FIELD: "M",
44
+ };
45
+ /* ------------------------------------------------------------------ */
46
+ /* GraphQL */
47
+ /* ------------------------------------------------------------------ */
48
+ const SUBGRAPH_PATH = "/graphql/knowledgeGraph";
49
+ function getEndpoint() {
50
+ const envUrl = typeof import.meta !== "undefined" &&
51
+ import.meta.env?.VITE_SUBGRAPH_URL;
52
+ if (envUrl)
53
+ return envUrl;
54
+ const port = globalThis.window?.location?.port;
55
+ if (port === "3000" || port === "3001") {
56
+ return `http://localhost:4001${SUBGRAPH_PATH}`;
57
+ }
58
+ return SUBGRAPH_PATH;
59
+ }
60
+ const ACTIVITY_QUERY = `
61
+ query Activity($driveId: ID!, $limit: Int) {
62
+ knowledgeGraphActivity(driveId: $driveId, limit: $limit) {
63
+ id documentId operationType timestamp index summary signerAddress signerApp
64
+ }
65
+ }
66
+ `;
67
+ async function fetchActivity(driveId, limit) {
68
+ try {
69
+ const res = await fetch(getEndpoint(), {
70
+ method: "POST",
71
+ headers: { "Content-Type": "application/json" },
72
+ body: JSON.stringify({
73
+ query: ACTIVITY_QUERY,
74
+ variables: { driveId, limit },
75
+ }),
76
+ });
77
+ if (!res.ok)
78
+ return [];
79
+ const json = (await res.json());
80
+ return json.data?.knowledgeGraphActivity ?? [];
81
+ }
82
+ catch {
83
+ return [];
84
+ }
85
+ }
86
+ /* ------------------------------------------------------------------ */
87
+ /* Component */
88
+ /* ------------------------------------------------------------------ */
89
+ export function ActivityView() {
90
+ const driveId = useSelectedDriveId();
91
+ const [operations, setOperations] = useState([]);
92
+ const [loading, setLoading] = useState(true);
93
+ const [filter, setFilter] = useState("all");
94
+ const endpoint = useMemo(() => getEndpoint(), []);
95
+ const loadActivity = useCallback(async () => {
96
+ if (!driveId)
97
+ return;
98
+ setLoading(true);
99
+ const data = await fetchActivity(driveId, 200);
100
+ setOperations(data);
101
+ setLoading(false);
102
+ }, [driveId]);
103
+ useEffect(() => {
104
+ void loadActivity();
105
+ }, [loadActivity]);
106
+ const filtered = useMemo(() => {
107
+ if (filter === "all")
108
+ return operations;
109
+ const allowedOps = FILTER_TYPES[filter].ops;
110
+ return operations.filter((op) => allowedOps.includes(op.operationType));
111
+ }, [operations, filter]);
112
+ // Group by date
113
+ const grouped = useMemo(() => {
114
+ const groups = new Map();
115
+ for (const op of filtered) {
116
+ const date = op.timestamp.slice(0, 10);
117
+ const existing = groups.get(date) ?? [];
118
+ existing.push(op);
119
+ groups.set(date, existing);
120
+ }
121
+ return groups;
122
+ }, [filtered]);
123
+ if (loading) {
124
+ return (_jsx("div", { className: "flex h-full items-center justify-center text-sm", style: { color: "var(--bai-text-muted)" }, children: "Loading activity..." }));
125
+ }
126
+ if (operations.length === 0) {
127
+ return (_jsxs("div", { className: "flex h-full flex-col items-center justify-center", children: [_jsxs("svg", { className: "mb-4 h-12 w-12", style: { color: "var(--bai-text-faint)", opacity: 0.5 }, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.5", children: [_jsx("circle", { cx: "12", cy: "12", r: "10" }), _jsx("path", { d: "M12 6v6l4 2" })] }), _jsx("p", { className: "text-sm", style: { color: "var(--bai-text-muted)" }, children: "No activity recorded yet" }), _jsx("p", { className: "mt-1 text-xs", style: { color: "var(--bai-text-faint)" }, children: "Edit notes to start tracking changes" })] }));
128
+ }
129
+ return (_jsxs("div", { className: "flex h-full flex-col p-4", children: [_jsxs("div", { className: "mb-4 flex items-center justify-between", children: [_jsxs("div", { children: [_jsx("h2", { className: "text-sm font-semibold", style: { color: "var(--bai-text)" }, children: "Vault Activity" }), _jsxs("p", { className: "text-xs", style: { color: "var(--bai-text-muted)" }, children: [filtered.length, " operation", filtered.length !== 1 ? "s" : "", filter !== "all" ? ` (${FILTER_TYPES[filter].label})` : ""] })] }), _jsx("div", { className: "flex items-center gap-2", children: _jsx("button", { type: "button", onClick: () => void loadActivity(), className: "rounded-md border px-3 py-1.5 text-xs transition-colors", style: {
130
+ borderColor: "var(--bai-border)",
131
+ color: "var(--bai-text-muted)",
132
+ backgroundColor: "var(--bai-surface)",
133
+ }, children: "Refresh" }) })] }), _jsx("div", { className: "mb-4 flex flex-wrap gap-1.5", children: Object.keys(FILTER_TYPES).map((key) => (_jsx("button", { type: "button", onClick: () => setFilter(key), className: "rounded-full px-3 py-1 text-xs transition-colors", style: {
134
+ backgroundColor: filter === key ? "var(--bai-accent)" : "var(--bai-hover)",
135
+ color: filter === key ? "#fff" : "var(--bai-text-muted)",
136
+ }, children: FILTER_TYPES[key].label }, key))) }), _jsx("div", { className: "flex-1 overflow-auto", children: [...grouped.entries()].map(([date, ops]) => (_jsxs("div", { className: "mb-6", children: [_jsx("h3", { className: "sticky top-0 z-10 mb-2 py-1 text-xs font-semibold uppercase tracking-wider", style: {
137
+ color: "var(--bai-text-muted)",
138
+ backgroundColor: "var(--bai-bg)",
139
+ }, children: formatDate(date) }), _jsx("div", { className: "space-y-1", children: ops.map((op) => (_jsx(OperationRow, { op: op }, op.id))) })] }, date))) })] }));
140
+ }
141
+ /* ------------------------------------------------------------------ */
142
+ /* Operation row */
143
+ /* ------------------------------------------------------------------ */
144
+ function OperationRow({ op }) {
145
+ const icon = OP_ICONS[op.operationType] ?? "?";
146
+ const time = op.timestamp.slice(11, 16);
147
+ return (_jsxs("button", { type: "button", onClick: () => setSelectedNode(op.documentId), className: "group flex w-full items-center gap-3 rounded-lg px-3 py-2 text-left transition-colors hover:bg-[var(--bai-hover)]", children: [_jsx("div", { className: "flex h-7 w-7 shrink-0 items-center justify-center rounded-md text-[10px] font-bold", style: {
148
+ backgroundColor: opColor(op.operationType) + "20",
149
+ color: opColor(op.operationType),
150
+ }, children: icon }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("p", { className: "truncate text-xs", style: { color: "var(--bai-text)" }, children: op.summary ?? op.operationType }), _jsxs("p", { className: "truncate text-[10px]", style: { color: "var(--bai-text-faint)" }, children: [op.documentId.slice(0, 8), "...", op.signerAddress || op.signerApp ? (_jsxs("span", { title: op.signerAddress ?? op.signerApp ?? "", className: "cursor-pointer", style: { color: "var(--bai-accent)", opacity: 0.7 }, onClick: (e) => {
151
+ e.stopPropagation();
152
+ const text = op.signerAddress ?? op.signerApp ?? "";
153
+ void navigator.clipboard.writeText(text);
154
+ }, children: [" by ", op.signerAddress
155
+ ? `${op.signerAddress.slice(0, 6)}...${op.signerAddress.slice(-4)}`
156
+ : op.signerApp, " ", _jsxs("svg", { className: "inline-block h-2.5 w-2.5 opacity-0 group-hover:opacity-50", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [_jsx("rect", { x: "9", y: "9", width: "13", height: "13", rx: "2" }), _jsx("path", { d: "M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1" })] })] })) : null] })] }), _jsx("span", { className: "shrink-0 text-[10px] font-mono", style: { color: "var(--bai-text-faint)" }, children: time }), _jsx("svg", { className: "h-3 w-3 shrink-0 opacity-0 transition-opacity group-hover:opacity-50", style: { color: "var(--bai-text-muted)" }, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: _jsx("path", { d: "M9 18l6-6-6-6" }) })] }));
157
+ }
158
+ /* ------------------------------------------------------------------ */
159
+ /* Helpers */
160
+ /* ------------------------------------------------------------------ */
161
+ function opColor(type) {
162
+ if (type.startsWith("SET_TITLE") || type.startsWith("SET_CONTENT"))
163
+ return "#f59e0b";
164
+ if (type.startsWith("ADD_LINK") || type.startsWith("REMOVE_LINK"))
165
+ return "#3b82f6";
166
+ if (type.startsWith("ADD_TOPIC") || type.startsWith("REMOVE_TOPIC"))
167
+ return "#10b981";
168
+ if (type === "SUBMIT_FOR_REVIEW" ||
169
+ type === "APPROVE_NOTE" ||
170
+ type === "REJECT_NOTE")
171
+ return "#a855f7";
172
+ if (type === "ARCHIVE_NOTE" || type === "RESTORE_NOTE")
173
+ return "#6b7280";
174
+ return "#64748b";
175
+ }
176
+ function formatDate(dateStr) {
177
+ const today = new Date().toISOString().slice(0, 10);
178
+ const yesterday = new Date(Date.now() - 86400000).toISOString().slice(0, 10);
179
+ if (dateStr === today)
180
+ return "Today";
181
+ if (dateStr === yesterday)
182
+ return "Yesterday";
183
+ return new Date(dateStr + "T00:00:00").toLocaleDateString("en-US", {
184
+ weekday: "short",
185
+ month: "short",
186
+ day: "numeric",
187
+ });
188
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"DriveExplorer.d.ts","sourceRoot":"","sources":["../../../../editors/knowledge-vault/components/DriveExplorer.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAiClD,wBAAgB,aAAa,CAAC,EAAE,QAAQ,EAAE,EAAE,WAAW,2CAiWtD"}
1
+ {"version":3,"file":"DriveExplorer.d.ts","sourceRoot":"","sources":["../../../../editors/knowledge-vault/components/DriveExplorer.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAmClD,wBAAgB,aAAa,CAAC,EAAE,QAAQ,EAAE,EAAE,WAAW,2CAmXtD"}
@@ -8,6 +8,7 @@ import { NoteList } from "./NoteList.js";
8
8
  import { SourceList } from "./SourceList.js";
9
9
  import { HealthDashboard } from "./HealthDashboard.js";
10
10
  import { SearchView } from "./SearchView.js";
11
+ import { ActivityView } from "./ActivityView.js";
11
12
  import { GettingStartedButton } from "./GettingStarted.js";
12
13
  import { useKnowledgeNotes } from "../hooks/use-knowledge-notes.js";
13
14
  import { useKnowledgeGraph, buildSyncPayload, } from "../hooks/use-knowledge-graph.js";
@@ -129,6 +130,11 @@ export function DriveExplorer({ children }) {
129
130
  badge: sourceCount > 0 ? sourceCount : undefined,
130
131
  icon: (_jsxs("svg", { className: "h-4 w-4", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [_jsx("path", { d: "M4 19.5A2.5 2.5 0 016.5 17H20" }), _jsx("path", { d: "M6.5 2H20v20H6.5A2.5 2.5 0 014 19.5v-15A2.5 2.5 0 016.5 2z" })] })),
131
132
  },
133
+ {
134
+ key: "activity",
135
+ label: "Activity",
136
+ icon: (_jsxs("svg", { className: "h-4 w-4", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [_jsx("circle", { cx: "12", cy: "12", r: "10" }), _jsx("path", { d: "M12 6v6l4 2" })] })),
137
+ },
132
138
  {
133
139
  key: "pipeline",
134
140
  label: "Pipeline",
@@ -161,7 +167,7 @@ export function DriveExplorer({ children }) {
161
167
  }, children: tab.badge }))] }, tab.key))), showDocumentEditor && (_jsxs("span", { className: "flex items-center gap-1.5 rounded-md px-3 py-1.5 text-xs font-medium", style: {
162
168
  backgroundColor: "var(--bai-hover)",
163
169
  color: "var(--bai-accent)",
164
- }, children: [_jsxs("svg", { className: "h-4 w-4", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [_jsx("path", { d: "M11 4H4a2 2 0 00-2 2v14a2 2 0 002 2h14a2 2 0 002-2v-7" }), _jsx("path", { d: "M18.5 2.5a2.121 2.121 0 013 3L12 15l-4 1 1-4 9.5-9.5z" })] }), "Editing"] }))] }), _jsxs("div", { className: "flex items-center gap-3", children: [hasGraphDoc && graphState && (_jsxs("span", { className: "text-[10px]", style: { color: "var(--bai-text-faint)" }, children: [graphState.nodes.length, "n / ", graphState.edges.length, "e"] })), _jsx(ThemeToggle, {}), _jsx(GettingStartedButton, {}), _jsx(CreateMenu, {})] })] }), _jsx("div", { className: "flex-1 overflow-auto", children: showDocumentEditor ? (_jsx("div", { className: "h-full", children: children })) : viewMode === "graph" ? (_jsx(GraphView, { notes: notes, graphState: graphState, mocs: mocs, tensions: tensions })) : viewMode === "search" ? (_jsx(SearchView, {})) : viewMode === "sources" ? (_jsx(SourceList, {})) : viewMode === "health" ? (_jsx(HealthDashboard, {})) : (_jsx(NoteList, { notes: notes })) })] })] }));
170
+ }, children: [_jsxs("svg", { className: "h-4 w-4", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [_jsx("path", { d: "M11 4H4a2 2 0 00-2 2v14a2 2 0 002 2h14a2 2 0 002-2v-7" }), _jsx("path", { d: "M18.5 2.5a2.121 2.121 0 013 3L12 15l-4 1 1-4 9.5-9.5z" })] }), "Editing"] }))] }), _jsxs("div", { className: "flex items-center gap-3", children: [hasGraphDoc && graphState && (_jsxs("span", { className: "text-[10px]", style: { color: "var(--bai-text-faint)" }, children: [graphState.nodes.length, "n / ", graphState.edges.length, "e"] })), _jsx(ThemeToggle, {}), _jsx(GettingStartedButton, {}), _jsx(CreateMenu, {})] })] }), _jsx("div", { className: "flex-1 overflow-auto", children: showDocumentEditor ? (_jsx("div", { className: "h-full", children: children })) : viewMode === "graph" ? (_jsx(GraphView, { notes: notes, graphState: graphState, mocs: mocs, tensions: tensions })) : viewMode === "search" ? (_jsx(SearchView, {})) : viewMode === "activity" ? (_jsx(ActivityView, {})) : viewMode === "sources" ? (_jsx(SourceList, {})) : viewMode === "health" ? (_jsx(HealthDashboard, {})) : (_jsx(NoteList, { notes: notes })) })] })] }));
165
171
  }
166
172
  const CREATE_ITEMS = [
167
173
  {
@@ -1 +1 @@
1
- {"version":3,"file":"SearchView.d.ts","sourceRoot":"","sources":["../../../../editors/knowledge-vault/components/SearchView.tsx"],"names":[],"mappings":"AA+CA,wBAAgB,UAAU,4CAmGzB"}
1
+ {"version":3,"file":"SearchView.d.ts","sourceRoot":"","sources":["../../../../editors/knowledge-vault/components/SearchView.tsx"],"names":[],"mappings":"AA+CA,wBAAgB,UAAU,4CAsGzB"}
@@ -43,19 +43,19 @@ export function SearchView() {
43
43
  backgroundColor: "var(--bai-surface)",
44
44
  borderColor: "var(--bai-border)",
45
45
  color: "var(--bai-text)",
46
- } }), query && (_jsx("button", { type: "button", onClick: () => setQuery(""), className: "absolute right-3 top-1/2 -translate-y-1/2 rounded p-0.5 transition-colors hover:opacity-80", style: { color: "var(--bai-text-muted)" }, children: _jsx("svg", { className: "h-4 w-4", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: _jsx("path", { d: "M18 6 6 18M6 6l12 12" }) }) }))] }), _jsx(ModeToggle, { mode: searchMode, onChange: setSearchMode })] }), query.trim() && (_jsxs("div", { className: "mb-3 flex items-center gap-2 text-xs", style: { color: "var(--bai-text-muted)" }, children: [loading ? (_jsx("span", { children: "Searching..." })) : (_jsxs(_Fragment, { children: [_jsxs("span", { children: [results.length, " result", results.length !== 1 ? "s" : ""] }), searchMode === "semantic" && results.length > 0 && (_jsx("span", { style: { opacity: 0.6 }, children: "\u2014 match % shows how closely each note relates to your query by meaning" }))] })), error && _jsx("span", { style: { color: "#ef4444" }, children: error })] })), _jsx("div", { className: "flex-1 overflow-auto", children: !query.trim() ? (_jsx(EmptyState, { topics: topics, onTopicClick: setQuery })) : (_jsx(ResultList, { results: results, searchMode: searchMode })) })] }));
46
+ } }), query && (_jsx("button", { type: "button", onClick: () => setQuery(""), className: "absolute right-3 top-1/2 -translate-y-1/2 rounded p-0.5 transition-colors hover:opacity-80", style: { color: "var(--bai-text-muted)" }, children: _jsx("svg", { className: "h-4 w-4", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: _jsx("path", { d: "M18 6 6 18M6 6l12 12" }) }) }))] }), _jsx(ModeToggle, { mode: searchMode, onChange: setSearchMode })] }), query.trim() && (_jsxs("div", { className: "mb-3 flex items-center gap-2 text-xs", style: { color: "var(--bai-text-muted)" }, children: [loading ? (_jsx("span", { children: "Searching..." })) : (_jsxs(_Fragment, { children: [_jsxs("span", { children: [results.length, " result", results.length !== 1 ? "s" : ""] }), (searchMode === "semantic" || searchMode === "hybrid") &&
47
+ results.length > 0 && (_jsxs("span", { style: { opacity: 0.6 }, children: ["\u2014 score shows relevance", searchMode === "hybrid"
48
+ ? " combining keyword + semantic matching"
49
+ : " by meaning"] }))] })), error && _jsx("span", { style: { color: "#ef4444" }, children: error })] })), _jsx("div", { className: "flex-1 overflow-auto", children: !query.trim() ? (_jsx(EmptyState, { topics: topics, onTopicClick: setQuery })) : (_jsx(ResultList, { results: results, searchMode: searchMode })) })] }));
47
50
  }
48
51
  /* ------------------------------------------------------------------ */
49
52
  /* Mode toggle */
50
53
  /* ------------------------------------------------------------------ */
51
54
  function ModeToggle({ mode, onChange, }) {
52
- return (_jsxs("div", { className: "flex shrink-0 overflow-hidden rounded-lg border text-xs", style: { borderColor: "var(--bai-border)" }, children: [_jsx("button", { type: "button", onClick: () => onChange("semantic"), className: "px-3 py-2 transition-colors", style: {
53
- backgroundColor: mode === "semantic" ? "var(--bai-accent)" : "var(--bai-surface)",
54
- color: mode === "semantic" ? "#fff" : "var(--bai-text-muted)",
55
- }, children: "Semantic" }), _jsx("button", { type: "button", onClick: () => onChange("keyword"), className: "px-3 py-2 transition-colors", style: {
56
- backgroundColor: mode === "keyword" ? "var(--bai-accent)" : "var(--bai-surface)",
57
- color: mode === "keyword" ? "#fff" : "var(--bai-text-muted)",
58
- }, children: "Keyword" })] }));
55
+ return (_jsx("div", { className: "flex shrink-0 overflow-hidden rounded-lg border text-xs", style: { borderColor: "var(--bai-border)" }, children: ["hybrid", "semantic", "keyword"].map((m) => (_jsx("button", { type: "button", onClick: () => onChange(m), className: "px-3 py-2 transition-colors capitalize", style: {
56
+ backgroundColor: mode === m ? "var(--bai-accent)" : "var(--bai-surface)",
57
+ color: mode === m ? "#fff" : "var(--bai-text-muted)",
58
+ }, children: m }, m))) }));
59
59
  }
60
60
  /* ------------------------------------------------------------------ */
61
61
  /* Empty state */
@@ -78,7 +78,7 @@ function ResultList({ results, searchMode, }) {
78
78
  if (results.length === 0) {
79
79
  return (_jsx("div", { className: "py-12 text-center text-sm", style: { color: "var(--bai-text-muted)" }, children: "No results found" }));
80
80
  }
81
- return (_jsx("div", { className: "space-y-2", children: results.map((result) => (_jsx(ResultCard, { result: result, showSimilarity: searchMode === "semantic" }, result.documentId))) }));
81
+ return (_jsx("div", { className: "space-y-2", children: results.map((result) => (_jsx(ResultCard, { result: result, showSimilarity: searchMode !== "keyword" }, result.documentId))) }));
82
82
  }
83
83
  /* ------------------------------------------------------------------ */
84
84
  /* Result card */
@@ -86,11 +86,18 @@ function ResultList({ results, searchMode, }) {
86
86
  function ResultCard({ result, showSimilarity, }) {
87
87
  const status = result.status ?? "DRAFT";
88
88
  const badgeStyle = STATUS_COLORS[status] ?? STATUS_COLORS.DRAFT;
89
- return (_jsxs("button", { type: "button", onClick: () => setSelectedNode(result.documentId), className: "group flex w-full items-start gap-3 rounded-xl border p-4 text-left transition-all border-[var(--bai-border)] bg-[var(--bai-surface)] hover:border-[var(--bai-accent)] hover:bg-[var(--bai-hover)]", children: [showSimilarity && result.similarity != null && (_jsxs("div", { className: "shrink-0 rounded-md px-2 py-1 text-center font-mono text-xs font-bold", title: "Semantic similarity \u2014 how closely this note's meaning matches your query (higher is better)", style: {
89
+ return (_jsxs("button", { type: "button", onClick: () => setSelectedNode(result.documentId), className: "group flex w-full items-start gap-3 rounded-xl border p-4 text-left transition-all border-[var(--bai-border)] bg-[var(--bai-surface)] hover:border-[var(--bai-accent)] hover:bg-[var(--bai-hover)]", children: [showSimilarity && result.similarity != null && (_jsxs("div", { className: "shrink-0 rounded-md px-2 py-1 text-center font-mono text-xs font-bold", title: "Relevance score \u2014 how closely this note matches your query (higher is better)", style: {
90
90
  color: similarityColor(result.similarity),
91
91
  backgroundColor: `${similarityColor(result.similarity)}15`,
92
92
  }, children: [(result.similarity * 100).toFixed(0), "%"] })), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsxs("div", { className: "mb-1 flex items-start justify-between gap-2", children: [_jsx("h4", { className: "truncate text-sm font-semibold text-[var(--bai-text)] group-hover:text-[var(--bai-accent)]", children: result.title ?? "Untitled" }), _jsx("span", { className: "shrink-0 rounded-full border px-2 py-0.5 text-[10px] font-medium", style: badgeStyle, children: status.replace("_", " ") })] }), result.description && (_jsx("p", { className: "mb-2 line-clamp-2 text-xs leading-relaxed", style: { color: "var(--bai-text-muted)" }, children: result.description })), _jsxs("div", { className: "flex flex-wrap items-center gap-1.5", children: [result.noteType && (_jsx("span", { className: "rounded px-1.5 py-0.5 text-[10px] font-medium", style: {
93
93
  background: "var(--bai-hover)",
94
94
  color: "var(--bai-text-muted)",
95
- }, children: result.noteType })), result.topics?.slice(0, 4).map((topic) => (_jsxs("span", { className: "text-[10px]", style: { color: "var(--bai-accent)", opacity: 0.6 }, children: ["#", topic] }, topic)))] })] }), _jsx("svg", { className: "mt-1 h-4 w-4 shrink-0 opacity-0 transition-opacity group-hover:opacity-60", style: { color: "var(--bai-text-muted)" }, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: _jsx("path", { d: "M9 18l6-6-6-6" }) })] }));
95
+ }, children: result.noteType })), result.topics?.slice(0, 4).map((topic) => (_jsxs("span", { className: "text-[10px]", style: { color: "var(--bai-accent)", opacity: 0.6 }, children: ["#", topic] }, topic))), result.matchedBy && result.matchedBy.length > 0 && (_jsx("span", { className: "ml-auto flex gap-1", children: result.matchedBy.map((m) => (_jsx("span", { className: "rounded px-1.5 py-0.5 text-[9px] font-medium", style: {
96
+ background: m === "semantic"
97
+ ? "rgba(139, 92, 246, 0.15)"
98
+ : "rgba(59, 130, 246, 0.15)",
99
+ color: m === "semantic"
100
+ ? "rgba(167, 139, 250, 1)"
101
+ : "rgba(147, 197, 253, 1)",
102
+ }, children: m }, m))) }))] })] }), _jsx("svg", { className: "mt-1 h-4 w-4 shrink-0 opacity-0 transition-opacity group-hover:opacity-60", style: { color: "var(--bai-text-muted)" }, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: _jsx("path", { d: "M9 18l6-6-6-6" }) })] }));
96
103
  }
@@ -6,12 +6,13 @@ export type SearchResult = {
6
6
  status: string | null;
7
7
  topics: string[];
8
8
  similarity?: number;
9
+ matchedBy?: string[];
9
10
  };
10
11
  export type TopicInfo = {
11
12
  name: string;
12
13
  noteCount: number;
13
14
  };
14
- export type SearchMode = "semantic" | "keyword";
15
+ export type SearchMode = "hybrid" | "semantic" | "keyword";
15
16
  export declare function useGraphSearch(): {
16
17
  query: string;
17
18
  setQuery: (q: string) => void;
@@ -1 +1 @@
1
- {"version":3,"file":"use-graph-search.d.ts","sourceRoot":"","sources":["../../../../editors/knowledge-vault/hooks/use-graph-search.ts"],"names":[],"mappings":"AAOA,MAAM,MAAM,YAAY,GAAG;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,SAAS,GAAG;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG,UAAU,GAAG,SAAS,CAAC;AAsHhD,wBAAgB,cAAc;;kBAgBtB,MAAM;;;;;;uBAQN,UAAU;EAoGjB"}
1
+ {"version":3,"file":"use-graph-search.d.ts","sourceRoot":"","sources":["../../../../editors/knowledge-vault/hooks/use-graph-search.ts"],"names":[],"mappings":"AAOA,MAAM,MAAM,YAAY,GAAG;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,SAAS,GAAG;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,UAAU,GAAG,SAAS,CAAC;AAkJ3D,wBAAgB,cAAc;;kBAgBtB,MAAM;;;;;;uBAQN,UAAU;EA4HjB"}
@@ -18,12 +18,21 @@ const SUBGRAPH_PATH = "/graphql/knowledgeGraph";
18
18
  * set VITE_SUBGRAPH_URL=https://switchboard-dev.powerhouse.xyz/graphql/knowledgeGraph
19
19
  * in your .env file.
20
20
  */
21
+ // Known Connect → Switchboard domain mappings for deployed environments
22
+ const DOMAIN_MAP = {
23
+ "connect-dev.powerhouse.xyz": "https://switchboard-dev.powerhouse.xyz/graphql/knowledgeGraph",
24
+ };
21
25
  function resolveEndpoint() {
22
26
  // Explicit override via env var
23
27
  const envUrl = typeof import.meta !== "undefined" &&
24
28
  import.meta.env?.VITE_SUBGRAPH_URL;
25
29
  if (envUrl)
26
30
  return envUrl;
31
+ // Check known Connect → Switchboard domain mappings
32
+ const hostname = globalThis.window?.location?.hostname;
33
+ if (hostname && DOMAIN_MAP[hostname]) {
34
+ return DOMAIN_MAP[hostname];
35
+ }
27
36
  // Vite dev server proxying to local reactor
28
37
  const port = globalThis.window?.location?.port;
29
38
  if (port === "3000" || port === "3001") {
@@ -70,6 +79,15 @@ const KEYWORD_SEARCH_QUERY = `
70
79
  }
71
80
  }
72
81
  `;
82
+ const HYBRID_SEARCH_QUERY = `
83
+ query HybridSearch($driveId: ID!, $query: String!, $limit: Int) {
84
+ knowledgeGraphHybridSearch(driveId: $driveId, query: $query, limit: $limit) {
85
+ node { documentId title description noteType status topics }
86
+ score
87
+ matchedBy
88
+ }
89
+ }
90
+ `;
73
91
  const TOPICS_QUERY = `
74
92
  query Topics($driveId: ID!) {
75
93
  knowledgeGraphTopics(driveId: $driveId) { name noteCount }
@@ -84,9 +102,14 @@ function loadSearchState() {
84
102
  const raw = sessionStorage.getItem(STORAGE_KEY);
85
103
  if (raw) {
86
104
  const parsed = JSON.parse(raw);
105
+ const mode = parsed.mode;
87
106
  return {
88
107
  query: parsed.query ?? "",
89
- mode: parsed.mode === "keyword" ? "keyword" : "semantic",
108
+ mode: mode === "keyword"
109
+ ? "keyword"
110
+ : mode === "semantic"
111
+ ? "semantic"
112
+ : "hybrid",
90
113
  };
91
114
  }
92
115
  }
@@ -142,7 +165,24 @@ export function useGraphSearch() {
142
165
  }
143
166
  setLoading(true);
144
167
  setError(null);
145
- if (mode === "semantic") {
168
+ if (mode === "hybrid") {
169
+ const data = await graphqlFetch(endpoint, HYBRID_SEARCH_QUERY, { driveId, query: q, limit: 20 });
170
+ if (data?.knowledgeGraphHybridSearch) {
171
+ const raw = data.knowledgeGraphHybridSearch;
172
+ // Normalize RRF scores to 0-1 range (top result = 1.0)
173
+ const maxScore = raw.length > 0 ? raw[0].score : 1;
174
+ setResults(raw.map((r) => ({
175
+ ...r.node,
176
+ similarity: maxScore > 0 ? r.score / maxScore : 0,
177
+ matchedBy: r.matchedBy,
178
+ })));
179
+ }
180
+ else {
181
+ setResults([]);
182
+ setError("Hybrid search unavailable. Try keyword mode.");
183
+ }
184
+ }
185
+ else if (mode === "semantic") {
146
186
  const data = await graphqlFetch(endpoint, SEMANTIC_SEARCH_QUERY, { driveId, query: q, limit: 20 });
147
187
  if (data?.knowledgeGraphSemanticSearch) {
148
188
  setResults(data.knowledgeGraphSemanticSearch.map((r) => ({
package/dist/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@powerhousedao/knowledge-note",
3
3
  "description": "Knowledge Note document model package for Powerhouse",
4
- "version": "1.0.4",
4
+ "version": "1.0.6",
5
5
  "license": "AGPL-3.0-only",
6
6
  "type": "module",
7
7
  "files": [
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../processors/graph-indexer/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,kCAAkC,CAAC;AACzE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,sCAAsC,CAAC;AAEjF,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AAItC,qBAAa,qBAAsB,SAAQ,qBAAqB,CAAC,EAAE,CAAC;WAClD,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM;IAItC,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC;IAI/B,YAAY,CACzB,UAAU,EAAE,oBAAoB,EAAE,GACjC,OAAO,CAAC,IAAI,CAAC;IAmKV,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;YAQrB,UAAU;CA8BzB"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../processors/graph-indexer/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,kCAAkC,CAAC;AACzE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,sCAAsC,CAAC;AAEjF,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AA+DtC,qBAAa,qBAAsB,SAAQ,qBAAqB,CAAC,EAAE,CAAC;WAClD,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM;IAItC,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC;IAI/B,YAAY,CACzB,UAAU,EAAE,oBAAoB,EAAE,GACjC,OAAO,CAAC,IAAI,CAAC;IAgMV,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;YAQrB,UAAU;CA8BzB"}
@@ -2,6 +2,62 @@ import { RelationalDbProcessor } from "@powerhousedao/shared/processors";
2
2
  import { up } from "./migrations.js";
3
3
  import { generateEmbedding } from "./embedder.js";
4
4
  import { upsertEmbedding, deleteEmbedding } from "./embedding-store.js";
5
+ function summarizeOperation(type, input) {
6
+ switch (type) {
7
+ case "SET_TITLE":
8
+ return `Title changed to "${truncate(input.title)}"`;
9
+ case "SET_DESCRIPTION":
10
+ return `Description updated`;
11
+ case "SET_CONTENT": {
12
+ const len = typeof input.content === "string" ? input.content.length : 0;
13
+ return `Content updated (${len} chars)`;
14
+ }
15
+ case "SET_NOTE_TYPE":
16
+ return `Type set to ${String(input.noteType)}`;
17
+ case "SET_STATUS":
18
+ return `Status changed to ${String(input.status)}`;
19
+ case "ADD_LINK":
20
+ return `Linked to "${truncate(input.targetTitle)}" (${s(input.linkType, "RELATES_TO")})`;
21
+ case "REMOVE_LINK":
22
+ return `Removed link ${s(input.id)}`;
23
+ case "UPDATE_LINK_TYPE":
24
+ return `Link type changed to ${s(input.linkType)}`;
25
+ case "ADD_TOPIC":
26
+ return `Added topic #${s(input.name)}`;
27
+ case "REMOVE_TOPIC":
28
+ return `Removed topic`;
29
+ case "SET_PROVENANCE":
30
+ return `Provenance set: ${s(input.author, "unknown")}, ${s(input.sourceOrigin)}`;
31
+ case "SUBMIT_FOR_REVIEW":
32
+ return `Submitted for review`;
33
+ case "APPROVE_NOTE":
34
+ return `Approved by ${s(input.actor, "unknown")}`;
35
+ case "REJECT_NOTE":
36
+ return `Rejected: ${truncate(input.comment)}`;
37
+ case "ARCHIVE_NOTE":
38
+ return `Archived`;
39
+ case "RESTORE_NOTE":
40
+ return `Restored from archive`;
41
+ case "SET_METADATA_FIELD":
42
+ return `Metadata: ${String(input.field)} = ${truncate(input.value)}`;
43
+ default:
44
+ return type;
45
+ }
46
+ }
47
+ /** Safely stringify an unknown value with optional fallback */
48
+ function s(val, fallback = "") {
49
+ if (val == null)
50
+ return fallback;
51
+ if (typeof val === "string")
52
+ return val;
53
+ if (typeof val === "number" || typeof val === "boolean")
54
+ return String(val);
55
+ return fallback;
56
+ }
57
+ function truncate(val, max = 60) {
58
+ const str = s(val);
59
+ return str.length > max ? str.slice(0, max) + "..." : str;
60
+ }
5
61
  export class GraphIndexerProcessor extends RelationalDbProcessor {
6
62
  static getNamespace(driveId) {
7
63
  return super.getNamespace(driveId);
@@ -28,6 +84,30 @@ export class GraphIndexerProcessor extends RelationalDbProcessor {
28
84
  // Only process knowledge-note documents
29
85
  if (context.documentType !== "bai/knowledge-note")
30
86
  continue;
87
+ // Index operation for history tracking
88
+ try {
89
+ const input = operation.action.input;
90
+ const signer = operation.action.context?.signer;
91
+ await this.relationalDb
92
+ .insertInto("graph_operations")
93
+ .values({
94
+ id: `${documentId}-${operation.index}`,
95
+ document_id: documentId,
96
+ operation_type: operation.action.type,
97
+ timestamp: operation.timestampUtcMs ?? new Date().toISOString(),
98
+ index: operation.index,
99
+ scope: context.scope ?? "global",
100
+ summary: summarizeOperation(operation.action.type, input),
101
+ input_json: JSON.stringify(input),
102
+ signer_address: signer?.user?.address || null,
103
+ signer_app: signer?.app?.name || null,
104
+ })
105
+ .onConflict((oc) => oc.column("id").doNothing())
106
+ .execute();
107
+ }
108
+ catch {
109
+ // non-critical — don't block state reconciliation
110
+ }
31
111
  // Collect last state per document
32
112
  if (context.resultingState) {
33
113
  lastByDocument.set(documentId, entry);
@@ -1 +1 @@
1
- {"version":3,"file":"migrations.d.ts","sourceRoot":"","sources":["../../../processors/graph-indexer/migrations.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kCAAkC,CAAC;AAEtE,wBAAsB,EAAE,CAAC,EAAE,EAAE,aAAa,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAmF9D;AAED,wBAAsB,IAAI,CAAC,EAAE,EAAE,aAAa,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAIhE"}
1
+ {"version":3,"file":"migrations.d.ts","sourceRoot":"","sources":["../../../processors/graph-indexer/migrations.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kCAAkC,CAAC;AAEtE,wBAAsB,EAAE,CAAC,EAAE,EAAE,aAAa,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAqI9D;AAED,wBAAsB,IAAI,CAAC,EAAE,EAAE,aAAa,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAKhE"}
@@ -73,8 +73,54 @@ export async function up(db) {
73
73
  .column("name")
74
74
  .ifNotExists()
75
75
  .execute();
76
+ // --- Operation history ---
77
+ await db.schema
78
+ .createTable("graph_operations")
79
+ .addColumn("id", "varchar(255)", (col) => col.primaryKey())
80
+ .addColumn("document_id", "varchar(255)", (col) => col.notNull())
81
+ .addColumn("operation_type", "varchar(100)", (col) => col.notNull())
82
+ .addColumn("timestamp", "varchar(50)", (col) => col.notNull())
83
+ .addColumn("index", "integer", (col) => col.notNull())
84
+ .addColumn("scope", "varchar(20)", (col) => col.notNull())
85
+ .addColumn("summary", "text")
86
+ .addColumn("input_json", "text")
87
+ .addColumn("signer_address", "varchar(255)")
88
+ .addColumn("signer_app", "varchar(255)")
89
+ .ifNotExists()
90
+ .execute();
91
+ await db.schema
92
+ .createIndex("idx_graph_ops_doc")
93
+ .on("graph_operations")
94
+ .column("document_id")
95
+ .ifNotExists()
96
+ .execute();
97
+ await db.schema
98
+ .createIndex("idx_graph_ops_timestamp")
99
+ .on("graph_operations")
100
+ .column("timestamp")
101
+ .ifNotExists()
102
+ .execute();
103
+ await db.schema
104
+ .createIndex("idx_graph_ops_type")
105
+ .on("graph_operations")
106
+ .column("operation_type")
107
+ .ifNotExists()
108
+ .execute();
109
+ // Add signer columns to graph_operations (idempotent)
110
+ for (const col of ["signer_address", "signer_app"]) {
111
+ try {
112
+ await db.schema
113
+ .alterTable("graph_operations")
114
+ .addColumn(col, "varchar(255)")
115
+ .execute();
116
+ }
117
+ catch {
118
+ // column likely already exists — ignore
119
+ }
120
+ }
76
121
  }
77
122
  export async function down(db) {
123
+ await db.schema.dropTable("graph_operations").ifExists().execute();
78
124
  await db.schema.dropTable("graph_topics").ifExists().execute();
79
125
  await db.schema.dropTable("graph_edges").ifExists().execute();
80
126
  await db.schema.dropTable("graph_nodes").ifExists().execute();
@@ -40,6 +40,22 @@ export interface RelatedByTopicResult {
40
40
  sharedTopics: string[];
41
41
  sharedTopicCount: number;
42
42
  }
43
+ export interface HybridSearchResult {
44
+ node: GraphNodeResult;
45
+ score: number;
46
+ matchedBy: string[];
47
+ }
48
+ export interface OperationRecord {
49
+ id: string;
50
+ documentId: string;
51
+ operationType: string;
52
+ timestamp: string;
53
+ index: number;
54
+ scope: string;
55
+ summary: string | null;
56
+ signerAddress: string | null;
57
+ signerApp: string | null;
58
+ }
43
59
  export declare function createGraphQuery(db: Kysely<DB>): {
44
60
  allNodes(): Promise<GraphNodeResult[]>;
45
61
  allEdges(): Promise<GraphEdgeResult[]>;
@@ -66,5 +82,18 @@ export declare function createGraphQuery(db: Kysely<DB>): {
66
82
  nodesByAuthor(author: string): Promise<GraphNodeResult[]>;
67
83
  nodesByOrigin(origin: string): Promise<GraphNodeResult[]>;
68
84
  recentNodes(limit?: number, since?: string): Promise<GraphNodeResult[]>;
85
+ /**
86
+ * Hybrid search: run keyword + semantic in parallel, merge via
87
+ * Reciprocal Rank Fusion (RRF). semanticResults must be pre-fetched
88
+ * from the embedding store by the caller.
89
+ */
90
+ hybridSearch(keywordQuery: string, semanticResults: Array<{
91
+ documentId: string;
92
+ similarity: number;
93
+ }>, limit?: number): Promise<HybridSearchResult[]>;
94
+ history(documentId: string, limit?: number): Promise<OperationRecord[]>;
95
+ activity(limit?: number, since?: string): Promise<OperationRecord[]>;
96
+ activityByType(operationType: string, limit?: number): Promise<OperationRecord[]>;
97
+ staleNodes(since: string, limit?: number): Promise<GraphNodeResult[]>;
69
98
  };
70
99
  //# sourceMappingURL=query.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../../../processors/graph-indexer/query.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../../../processors/graph-indexer/query.ts"],"names":[],"mappings":"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"}
@@ -24,6 +24,19 @@ function rowToEdge(row) {
24
24
  updatedAt: row.updated_at,
25
25
  };
26
26
  }
27
+ function rowToOperation(row) {
28
+ return {
29
+ id: row.id,
30
+ documentId: row.document_id,
31
+ operationType: row.operation_type,
32
+ timestamp: row.timestamp,
33
+ index: row.index,
34
+ scope: row.scope,
35
+ summary: row.summary,
36
+ signerAddress: row.signer_address,
37
+ signerApp: row.signer_app,
38
+ };
39
+ }
27
40
  export function createGraphQuery(db) {
28
41
  return {
29
42
  async allNodes() {
@@ -372,5 +385,96 @@ export function createGraphQuery(db) {
372
385
  const rows = await query.execute();
373
386
  return rows.map(rowToNode);
374
387
  },
388
+ /**
389
+ * Hybrid search: run keyword + semantic in parallel, merge via
390
+ * Reciprocal Rank Fusion (RRF). semanticResults must be pre-fetched
391
+ * from the embedding store by the caller.
392
+ */
393
+ async hybridSearch(keywordQuery, semanticResults, limit = 20) {
394
+ const K = 60;
395
+ const scores = new Map();
396
+ // Keyword leg
397
+ const keywordResults = await this.fullSearch(keywordQuery, limit * 2);
398
+ keywordResults.forEach((node, rank) => {
399
+ const existing = scores.get(node.documentId) ?? {
400
+ score: 0,
401
+ matchedBy: [],
402
+ };
403
+ existing.score += 1 / (K + rank);
404
+ existing.matchedBy.push("keyword");
405
+ existing.node = node;
406
+ scores.set(node.documentId, existing);
407
+ });
408
+ // Semantic leg
409
+ for (let rank = 0; rank < semanticResults.length; rank++) {
410
+ const sr = semanticResults[rank];
411
+ const existing = scores.get(sr.documentId) ?? {
412
+ score: 0,
413
+ matchedBy: [],
414
+ };
415
+ existing.score += 1 / (K + rank);
416
+ if (!existing.matchedBy.includes("semantic")) {
417
+ existing.matchedBy.push("semantic");
418
+ }
419
+ // Fetch node data if we don't have it from keyword results
420
+ if (!existing.node) {
421
+ existing.node = await this.nodeByDocumentId(sr.documentId);
422
+ }
423
+ scores.set(sr.documentId, existing);
424
+ }
425
+ return [...scores.values()]
426
+ .filter((e) => !!e.node)
427
+ .sort((a, b) => b.score - a.score)
428
+ .slice(0, limit)
429
+ .map(({ score, matchedBy, node }) => ({ node, score, matchedBy }));
430
+ },
431
+ // ---- Operation history queries ----
432
+ async history(documentId, limit = 50) {
433
+ const rows = await db
434
+ .selectFrom("graph_operations")
435
+ .where("document_id", "=", documentId)
436
+ .orderBy("index", "desc")
437
+ .selectAll()
438
+ .limit(limit)
439
+ .execute();
440
+ return rows.map(rowToOperation);
441
+ },
442
+ async activity(limit = 50, since) {
443
+ let query = db
444
+ .selectFrom("graph_operations")
445
+ .orderBy("timestamp", "desc")
446
+ .selectAll()
447
+ .limit(limit);
448
+ if (since) {
449
+ query = query.where("timestamp", ">", since);
450
+ }
451
+ const rows = await query.execute();
452
+ return rows.map(rowToOperation);
453
+ },
454
+ async activityByType(operationType, limit = 50) {
455
+ const rows = await db
456
+ .selectFrom("graph_operations")
457
+ .where("operation_type", "=", operationType)
458
+ .orderBy("timestamp", "desc")
459
+ .selectAll()
460
+ .limit(limit)
461
+ .execute();
462
+ return rows.map(rowToOperation);
463
+ },
464
+ async staleNodes(since, limit = 50) {
465
+ // Nodes with no operations after `since` but that have neighbors
466
+ // who DO have operations after `since`
467
+ const staleRows = await db
468
+ .selectFrom("graph_nodes")
469
+ .where("document_id", "not in", db
470
+ .selectFrom("graph_operations")
471
+ .where("timestamp", ">", since)
472
+ .select("document_id"))
473
+ .where("updated_at", "<", since)
474
+ .selectAll()
475
+ .limit(limit)
476
+ .execute();
477
+ return staleRows.map(rowToNode);
478
+ },
375
479
  };
376
480
  }
@@ -25,9 +25,22 @@ export interface GraphEdge {
25
25
  target_title: string | null;
26
26
  updated_at: string;
27
27
  }
28
+ export interface GraphOperation {
29
+ id: string;
30
+ document_id: string;
31
+ operation_type: string;
32
+ timestamp: string;
33
+ index: number;
34
+ scope: string;
35
+ summary: string | null;
36
+ input_json: string | null;
37
+ signer_address: string | null;
38
+ signer_app: string | null;
39
+ }
28
40
  export interface DB {
29
41
  graph_nodes: GraphNode;
30
42
  graph_edges: GraphEdge;
31
43
  graph_topics: GraphTopic;
44
+ graph_operations: GraphOperation;
32
45
  }
33
46
  //# sourceMappingURL=schema.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../../processors/graph-indexer/schema.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,kBAAkB,EAAE,MAAM,CAAC;IAC3B,kBAAkB,EAAE,MAAM,CAAC;IAC3B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,EAAE;IACjB,WAAW,EAAE,SAAS,CAAC;IACvB,WAAW,EAAE,SAAS,CAAC;IACvB,YAAY,EAAE,UAAU,CAAC;CAC1B"}
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../../processors/graph-indexer/schema.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,kBAAkB,EAAE,MAAM,CAAC;IAC3B,kBAAkB,EAAE,MAAM,CAAC;IAC3B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED,MAAM,WAAW,EAAE;IACjB,WAAW,EAAE,SAAS,CAAC;IACvB,WAAW,EAAE,SAAS,CAAC;IACvB,YAAY,EAAE,UAAU,CAAC;IACzB,gBAAgB,EAAE,cAAc,CAAC;CAClC"}
package/dist/style.css CHANGED
@@ -1509,6 +1509,13 @@
1509
1509
  }
1510
1510
  }
1511
1511
  }
1512
+ .group-hover\:opacity-50 {
1513
+ &:is(:where(.group):hover *) {
1514
+ @media (hover: hover) {
1515
+ opacity: 50%;
1516
+ }
1517
+ }
1518
+ }
1512
1519
  .group-hover\:opacity-60 {
1513
1520
  &:is(:where(.group):hover *) {
1514
1521
  @media (hover: hover) {
@@ -360,6 +360,61 @@ export declare class KnowledgeGraphSubgraph extends BaseSubgraph {
360
360
  };
361
361
  similarity: number;
362
362
  }[]>;
363
+ knowledgeGraphHybridSearch: (_: unknown, args: {
364
+ driveId: string;
365
+ query: string;
366
+ limit?: number;
367
+ }) => Promise<{
368
+ node: {
369
+ _driveId: string;
370
+ id: string;
371
+ documentId: string;
372
+ title: string | null;
373
+ description: string | null;
374
+ noteType: string | null;
375
+ status: string | null;
376
+ content: string | null;
377
+ author: string | null;
378
+ sourceOrigin: string | null;
379
+ createdAt: string | null;
380
+ updatedAt: string;
381
+ };
382
+ score: number;
383
+ matchedBy: string[];
384
+ }[]>;
385
+ knowledgeGraphHistory: (_: unknown, args: {
386
+ driveId: string;
387
+ documentId: string;
388
+ limit?: number;
389
+ }) => Promise<import("../../processors/graph-indexer/query.js").OperationRecord[]>;
390
+ knowledgeGraphActivity: (_: unknown, args: {
391
+ driveId: string;
392
+ limit?: number;
393
+ since?: string;
394
+ }) => Promise<import("../../processors/graph-indexer/query.js").OperationRecord[]>;
395
+ knowledgeGraphActivityByType: (_: unknown, args: {
396
+ driveId: string;
397
+ operationType: string;
398
+ limit?: number;
399
+ }) => Promise<import("../../processors/graph-indexer/query.js").OperationRecord[]>;
400
+ knowledgeGraphStale: (_: unknown, args: {
401
+ driveId: string;
402
+ since: string;
403
+ limit?: number;
404
+ }) => Promise<{
405
+ _driveId: string;
406
+ id: string;
407
+ documentId: string;
408
+ title: string | null;
409
+ description: string | null;
410
+ noteType: string | null;
411
+ status: string | null;
412
+ content: string | null;
413
+ author: string | null;
414
+ sourceOrigin: string | null;
415
+ createdAt: string | null;
416
+ updatedAt: string;
417
+ }[]>;
363
418
  knowledgeGraphDebug: (_: unknown, args: {
364
419
  driveId: string;
365
420
  }) => Promise<{
@@ -1 +1 @@
1
- {"version":3,"file":"subgraph.d.ts","sourceRoot":"","sources":["../../../subgraphs/knowledge-graph/subgraph.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"subgraph.d.ts","sourceRoot":"","sources":["../../../subgraphs/knowledge-graph/subgraph.ts"],"names":[],"mappings":"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"}
@@ -129,6 +129,32 @@ export class KnowledgeGraphSubgraph extends BaseSubgraph {
129
129
  documentId: String!
130
130
  limit: Int
131
131
  ): [SemanticResult!]!
132
+ knowledgeGraphHybridSearch(
133
+ driveId: ID!
134
+ query: String!
135
+ limit: Int
136
+ ): [HybridResult!]!
137
+
138
+ knowledgeGraphHistory(
139
+ driveId: ID!
140
+ documentId: String!
141
+ limit: Int
142
+ ): [OperationRecord!]!
143
+ knowledgeGraphActivity(
144
+ driveId: ID!
145
+ limit: Int
146
+ since: String
147
+ ): [OperationRecord!]!
148
+ knowledgeGraphActivityByType(
149
+ driveId: ID!
150
+ operationType: String!
151
+ limit: Int
152
+ ): [OperationRecord!]!
153
+ knowledgeGraphStale(
154
+ driveId: ID!
155
+ since: String!
156
+ limit: Int
157
+ ): [KnowledgeGraphNode!]!
132
158
 
133
159
  """
134
160
  Debug: raw processor DB tables
@@ -147,6 +173,24 @@ export class KnowledgeGraphSubgraph extends BaseSubgraph {
147
173
  similarity: Float!
148
174
  }
149
175
 
176
+ type HybridResult {
177
+ node: KnowledgeGraphNode!
178
+ score: Float!
179
+ matchedBy: [String!]!
180
+ }
181
+
182
+ type OperationRecord {
183
+ id: String!
184
+ documentId: String!
185
+ operationType: String!
186
+ timestamp: String!
187
+ index: Int!
188
+ scope: String!
189
+ summary: String
190
+ signerAddress: String
191
+ signerApp: String
192
+ }
193
+
150
194
  type GraphDebugInfo {
151
195
  rawNodeCount: Int!
152
196
  rawEdgeCount: Int!
@@ -331,6 +375,34 @@ export class KnowledgeGraphSubgraph extends BaseSubgraph {
331
375
  }
332
376
  return semanticResults.slice(0, args.limit ?? 10);
333
377
  },
378
+ knowledgeGraphHybridSearch: async (_, args) => {
379
+ const queryEmbedding = await generateEmbedding(args.query);
380
+ const semanticResults = await searchSimilar(queryEmbedding, (args.limit ?? 20) * 2);
381
+ const graphQuery = this.getQuery(args.driveId);
382
+ const hybridResults = await graphQuery.hybridSearch(args.query, semanticResults, args.limit ?? 20);
383
+ return hybridResults.map((r) => ({
384
+ node: { ...r.node, _driveId: args.driveId },
385
+ score: r.score,
386
+ matchedBy: r.matchedBy,
387
+ }));
388
+ },
389
+ knowledgeGraphHistory: async (_, args) => {
390
+ const query = this.getQuery(args.driveId);
391
+ return query.history(args.documentId, args.limit ?? 50);
392
+ },
393
+ knowledgeGraphActivity: async (_, args) => {
394
+ const query = this.getQuery(args.driveId);
395
+ return query.activity(args.limit ?? 50, args.since ?? undefined);
396
+ },
397
+ knowledgeGraphActivityByType: async (_, args) => {
398
+ const query = this.getQuery(args.driveId);
399
+ return query.activityByType(args.operationType, args.limit ?? 50);
400
+ },
401
+ knowledgeGraphStale: async (_, args) => {
402
+ const query = this.getQuery(args.driveId);
403
+ const nodes = await query.staleNodes(args.since, args.limit ?? 50);
404
+ return nodes.map((n) => ({ ...n, _driveId: args.driveId }));
405
+ },
334
406
  knowledgeGraphDebug: async (_, args) => {
335
407
  const namespace = GraphIndexerProcessor.getNamespace(args.driveId);
336
408
  try {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@powerhousedao/knowledge-note",
3
3
  "description": "Knowledge Note document model package for Powerhouse",
4
- "version": "1.0.4",
4
+ "version": "1.0.6",
5
5
  "license": "AGPL-3.0-only",
6
6
  "type": "module",
7
7
  "files": [