mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-06-26 21:39:43 +02:00
feat(filesystem): enhance agent filesystem API with searchSpaceId support for improved context handling
This commit is contained in:
parent
86e2dc8a5d
commit
27e16231c1
10 changed files with 349 additions and 85 deletions
|
|
@ -658,7 +658,7 @@ export default function NewChatPage() {
|
|||
|
||||
try {
|
||||
const backendUrl = process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL || "http://localhost:8000";
|
||||
const selection = await getAgentFilesystemSelection();
|
||||
const selection = await getAgentFilesystemSelection(searchSpaceId);
|
||||
if (
|
||||
selection.filesystem_mode === "desktop_local_folder" &&
|
||||
(!selection.local_filesystem_mounts ||
|
||||
|
|
@ -1088,7 +1088,7 @@ export default function NewChatPage() {
|
|||
|
||||
try {
|
||||
const backendUrl = process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL || "http://localhost:8000";
|
||||
const selection = await getAgentFilesystemSelection();
|
||||
const selection = await getAgentFilesystemSelection(searchSpaceId);
|
||||
const response = await fetch(`${backendUrl}/api/v1/threads/${resumeThreadId}/resume`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
|
|
@ -1424,7 +1424,7 @@ export default function NewChatPage() {
|
|||
]);
|
||||
|
||||
try {
|
||||
const selection = await getAgentFilesystemSelection();
|
||||
const selection = await getAgentFilesystemSelection(searchSpaceId);
|
||||
const response = await fetch(getRegenerateUrl(threadId), {
|
||||
method: "POST",
|
||||
headers: {
|
||||
|
|
|
|||
|
|
@ -124,7 +124,10 @@ export function EditorPanelContent({
|
|||
if (!electronAPI?.readAgentLocalFileText) {
|
||||
throw new Error("Local file editor is available only in desktop mode.");
|
||||
}
|
||||
const readResult = await electronAPI.readAgentLocalFileText(localFilePath);
|
||||
const readResult = await electronAPI.readAgentLocalFileText(
|
||||
localFilePath,
|
||||
searchSpaceId
|
||||
);
|
||||
if (!readResult.ok) {
|
||||
throw new Error(readResult.error || "Failed to read local file");
|
||||
}
|
||||
|
|
@ -226,7 +229,7 @@ export function EditorPanelContent({
|
|||
}
|
||||
}, [editorDoc?.source_markdown]);
|
||||
|
||||
const handleSave = useCallback(async (options?: { silent?: boolean }) => {
|
||||
const handleSave = useCallback(async (_options?: { silent?: boolean }) => {
|
||||
setSaving(true);
|
||||
try {
|
||||
if (isLocalFileMode) {
|
||||
|
|
@ -239,7 +242,8 @@ export function EditorPanelContent({
|
|||
const contentToSave = markdownRef.current;
|
||||
const writeResult = await electronAPI.writeAgentLocalFileText(
|
||||
localFilePath,
|
||||
contentToSave
|
||||
contentToSave,
|
||||
searchSpaceId
|
||||
);
|
||||
if (!writeResult.ok) {
|
||||
throw new Error(writeResult.error || "Failed to save local file");
|
||||
|
|
|
|||
|
|
@ -214,7 +214,7 @@ function AuthenticatedDocumentsSidebar({
|
|||
if (!electronAPI?.getAgentFilesystemSettings) return;
|
||||
let mounted = true;
|
||||
electronAPI
|
||||
.getAgentFilesystemSettings()
|
||||
.getAgentFilesystemSettings(searchSpaceId)
|
||||
.then((settings: FilesystemSettings) => {
|
||||
if (!mounted) return;
|
||||
setFilesystemSettings(settings);
|
||||
|
|
@ -230,7 +230,7 @@ function AuthenticatedDocumentsSidebar({
|
|||
return () => {
|
||||
mounted = false;
|
||||
};
|
||||
}, [electronAPI]);
|
||||
}, [electronAPI, searchSpaceId]);
|
||||
|
||||
const hasLocalFilesystemTrust = useCallback(() => {
|
||||
try {
|
||||
|
|
@ -253,10 +253,10 @@ function AuthenticatedDocumentsSidebar({
|
|||
const updated = await electronAPI.setAgentFilesystemSettings({
|
||||
mode: "desktop_local_folder",
|
||||
localRootPaths: nextLocalRootPaths,
|
||||
});
|
||||
}, searchSpaceId);
|
||||
setFilesystemSettings(updated);
|
||||
},
|
||||
[electronAPI, localRootPaths]
|
||||
[electronAPI, localRootPaths, searchSpaceId]
|
||||
);
|
||||
|
||||
const runPickLocalRoot = useCallback(async () => {
|
||||
|
|
@ -285,10 +285,10 @@ function AuthenticatedDocumentsSidebar({
|
|||
const updated = await electronAPI.setAgentFilesystemSettings({
|
||||
mode: "desktop_local_folder",
|
||||
localRootPaths: localRootPaths.filter((rootPath) => rootPath !== rootPathToRemove),
|
||||
});
|
||||
}, searchSpaceId);
|
||||
setFilesystemSettings(updated);
|
||||
},
|
||||
[electronAPI, localRootPaths]
|
||||
[electronAPI, localRootPaths, searchSpaceId]
|
||||
);
|
||||
|
||||
const handleClearFilesystemRoots = useCallback(async () => {
|
||||
|
|
@ -296,19 +296,19 @@ function AuthenticatedDocumentsSidebar({
|
|||
const updated = await electronAPI.setAgentFilesystemSettings({
|
||||
mode: "desktop_local_folder",
|
||||
localRootPaths: [],
|
||||
});
|
||||
}, searchSpaceId);
|
||||
setFilesystemSettings(updated);
|
||||
}, [electronAPI]);
|
||||
}, [electronAPI, searchSpaceId]);
|
||||
|
||||
const handleFilesystemTabChange = useCallback(
|
||||
async (tab: "cloud" | "local") => {
|
||||
if (!electronAPI?.setAgentFilesystemSettings) return;
|
||||
const updated = await electronAPI.setAgentFilesystemSettings({
|
||||
mode: tab === "cloud" ? "cloud" : "desktop_local_folder",
|
||||
});
|
||||
}, searchSpaceId);
|
||||
setFilesystemSettings(updated);
|
||||
},
|
||||
[electronAPI]
|
||||
[electronAPI, searchSpaceId]
|
||||
);
|
||||
|
||||
// AI File Sort state
|
||||
|
|
@ -1323,6 +1323,7 @@ function AuthenticatedDocumentsSidebar({
|
|||
<LocalFilesystemBrowser
|
||||
rootPaths={localRootPaths}
|
||||
searchSpaceId={searchSpaceId}
|
||||
active={currentFilesystemTab === "local"}
|
||||
searchQuery={debouncedLocalSearch.trim() || undefined}
|
||||
onOpenFile={(localFilePath) => {
|
||||
openEditorPanel({
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import { getSupportedExtensionsSet } from "@/lib/supported-extensions";
|
|||
interface LocalFilesystemBrowserProps {
|
||||
rootPaths: string[];
|
||||
searchSpaceId: number;
|
||||
active?: boolean;
|
||||
searchQuery?: string;
|
||||
onOpenFile: (fullPath: string) => void;
|
||||
}
|
||||
|
|
@ -75,6 +76,7 @@ function toMountedVirtualPath(mount: string, relativePath: string): string {
|
|||
export function LocalFilesystemBrowser({
|
||||
rootPaths,
|
||||
searchSpaceId,
|
||||
active = true,
|
||||
searchQuery,
|
||||
onOpenFile,
|
||||
}: LocalFilesystemBrowserProps) {
|
||||
|
|
@ -84,13 +86,36 @@ export function LocalFilesystemBrowser({
|
|||
const [mountByRootKey, setMountByRootKey] = useState<Map<string, string>>(new Map());
|
||||
const [mountStatus, setMountStatus] = useState<MountLoadStatus>("idle");
|
||||
const [mountRefreshInFlight, setMountRefreshInFlight] = useState(false);
|
||||
const lastLoadedRootsSignatureRef = useRef<string>("");
|
||||
const hasLoadedMountsOnceRef = useRef(false);
|
||||
const hasResolvedAtLeastOneRootRef = useRef(false);
|
||||
const supportedExtensions = useMemo(() => Array.from(getSupportedExtensionsSet()), []);
|
||||
const isWindowsPlatform = electronAPI?.versions.platform === "win32";
|
||||
|
||||
useEffect(() => {
|
||||
if (!electronAPI?.listFolderFiles) return;
|
||||
if (!active) return;
|
||||
if (!electronAPI?.listAgentFilesystemFiles) {
|
||||
for (const rootPath of rootPaths) {
|
||||
setRootStateMap((prev) => ({
|
||||
...prev,
|
||||
[rootPath]: {
|
||||
loading: false,
|
||||
error: "Desktop app update required for local mode browsing.",
|
||||
files: [],
|
||||
},
|
||||
}));
|
||||
}
|
||||
return;
|
||||
}
|
||||
const rootsSignature = rootPaths
|
||||
.map((rootPath) => normalizeRootPathForLookup(rootPath, isWindowsPlatform))
|
||||
.sort()
|
||||
.join("|");
|
||||
const settingsSignature = `${searchSpaceId}:${rootsSignature}`;
|
||||
if (settingsSignature === lastLoadedRootsSignatureRef.current) {
|
||||
return;
|
||||
}
|
||||
lastLoadedRootsSignatureRef.current = settingsSignature;
|
||||
let cancelled = false;
|
||||
|
||||
for (const rootPath of rootPaths) {
|
||||
|
|
@ -107,14 +132,11 @@ export function LocalFilesystemBrowser({
|
|||
void Promise.all(
|
||||
rootPaths.map(async (rootPath) => {
|
||||
try {
|
||||
const files = (await electronAPI.listFolderFiles({
|
||||
path: rootPath,
|
||||
name: getFolderDisplayName(rootPath),
|
||||
const files = (await electronAPI.listAgentFilesystemFiles({
|
||||
rootPath,
|
||||
searchSpaceId,
|
||||
excludePatterns: DEFAULT_EXCLUDE_PATTERNS,
|
||||
fileExtensions: supportedExtensions,
|
||||
rootFolderId: null,
|
||||
searchSpaceId,
|
||||
active: true,
|
||||
})) as LocalFolderFileEntry[];
|
||||
if (cancelled) return;
|
||||
setRootStateMap((prev) => ({
|
||||
|
|
@ -142,7 +164,7 @@ export function LocalFilesystemBrowser({
|
|||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}, [electronAPI, rootPaths, searchSpaceId, supportedExtensions]);
|
||||
}, [active, electronAPI, isWindowsPlatform, rootPaths, searchSpaceId, supportedExtensions]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!electronAPI?.getAgentFilesystemMounts) {
|
||||
|
|
@ -165,7 +187,7 @@ export function LocalFilesystemBrowser({
|
|||
setMountRefreshInFlight(true);
|
||||
}
|
||||
void electronAPI
|
||||
.getAgentFilesystemMounts()
|
||||
.getAgentFilesystemMounts(searchSpaceId)
|
||||
.then((mounts: LocalRootMount[]) => {
|
||||
if (cancelled) return;
|
||||
const next = new Map<string, string>();
|
||||
|
|
@ -191,7 +213,7 @@ export function LocalFilesystemBrowser({
|
|||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}, [electronAPI, isWindowsPlatform, rootPaths]);
|
||||
}, [electronAPI, isWindowsPlatform, rootPaths, searchSpaceId]);
|
||||
|
||||
const treeByRoot = useMemo(() => {
|
||||
const query = searchQuery?.trim().toLowerCase() ?? "";
|
||||
|
|
|
|||
|
|
@ -22,15 +22,17 @@ export function getClientPlatform(): ClientPlatform {
|
|||
return window.electronAPI ? "desktop" : "web";
|
||||
}
|
||||
|
||||
export async function getAgentFilesystemSelection(): Promise<AgentFilesystemSelection> {
|
||||
export async function getAgentFilesystemSelection(
|
||||
searchSpaceId?: number | null
|
||||
): Promise<AgentFilesystemSelection> {
|
||||
const platform = getClientPlatform();
|
||||
if (platform !== "desktop" || !window.electronAPI?.getAgentFilesystemSettings) {
|
||||
return { ...DEFAULT_SELECTION, client_platform: platform };
|
||||
}
|
||||
try {
|
||||
const settings = await window.electronAPI.getAgentFilesystemSettings();
|
||||
const settings = await window.electronAPI.getAgentFilesystemSettings(searchSpaceId);
|
||||
if (settings.mode === "desktop_local_folder") {
|
||||
const mounts = await window.electronAPI.getAgentFilesystemMounts?.();
|
||||
const mounts = await window.electronAPI.getAgentFilesystemMounts?.(searchSpaceId);
|
||||
const localFilesystemMounts =
|
||||
mounts?.map((entry) => ({
|
||||
mount_id: entry.mount,
|
||||
|
|
|
|||
24
surfsense_web/types/window.d.ts
vendored
24
surfsense_web/types/window.d.ts
vendored
|
|
@ -54,6 +54,13 @@ interface AgentFilesystemMount {
|
|||
rootPath: string;
|
||||
}
|
||||
|
||||
interface AgentFilesystemListOptions {
|
||||
rootPath: string;
|
||||
searchSpaceId?: number | null;
|
||||
excludePatterns?: string[] | null;
|
||||
fileExtensions?: string[] | null;
|
||||
}
|
||||
|
||||
interface LocalTextFileResult {
|
||||
ok: boolean;
|
||||
path: string;
|
||||
|
|
@ -114,10 +121,14 @@ interface ElectronAPI {
|
|||
// Browse files/folders via native dialogs
|
||||
browseFiles: () => Promise<string[] | null>;
|
||||
readLocalFiles: (paths: string[]) => Promise<LocalFileData[]>;
|
||||
readAgentLocalFileText: (virtualPath: string) => Promise<LocalTextFileResult>;
|
||||
readAgentLocalFileText: (
|
||||
virtualPath: string,
|
||||
searchSpaceId?: number | null
|
||||
) => Promise<LocalTextFileResult>;
|
||||
writeAgentLocalFileText: (
|
||||
virtualPath: string,
|
||||
content: string
|
||||
content: string,
|
||||
searchSpaceId?: number | null
|
||||
) => Promise<LocalTextFileResult>;
|
||||
// Auth token sync across windows
|
||||
getAuthTokens: () => Promise<{ bearer: string; refresh: string } | null>;
|
||||
|
|
@ -151,12 +162,15 @@ interface ElectronAPI {
|
|||
platform: string;
|
||||
}>;
|
||||
// Agent filesystem mode
|
||||
getAgentFilesystemSettings: () => Promise<AgentFilesystemSettings>;
|
||||
getAgentFilesystemMounts: () => Promise<AgentFilesystemMount[]>;
|
||||
getAgentFilesystemSettings: (searchSpaceId?: number | null) => Promise<AgentFilesystemSettings>;
|
||||
getAgentFilesystemMounts: (searchSpaceId?: number | null) => Promise<AgentFilesystemMount[]>;
|
||||
listAgentFilesystemFiles: (
|
||||
options: AgentFilesystemListOptions
|
||||
) => Promise<FolderFileEntry[]>;
|
||||
setAgentFilesystemSettings: (settings: {
|
||||
mode?: AgentFilesystemMode;
|
||||
localRootPaths?: string[] | null;
|
||||
}) => Promise<AgentFilesystemSettings>;
|
||||
}, searchSpaceId?: number | null) => Promise<AgentFilesystemSettings>;
|
||||
pickAgentFilesystemRoot: () => Promise<string | null>;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue