diff --git a/surfsense_backend/app/agents/autocomplete/autocomplete_agent.py b/surfsense_backend/app/agents/autocomplete/autocomplete_agent.py
index 928a133cc..c6a071b0f 100644
--- a/surfsense_backend/app/agents/autocomplete/autocomplete_agent.py
+++ b/surfsense_backend/app/agents/autocomplete/autocomplete_agent.py
@@ -16,7 +16,8 @@ from __future__ import annotations
import asyncio
import logging
import uuid
-from typing import Any, AsyncGenerator
+from collections.abc import AsyncGenerator
+from typing import Any
from deepagents.graph import BASE_AGENT_PROMPT
from deepagents.middleware.patch_tool_calls import PatchToolCallsMiddleware
@@ -122,6 +123,7 @@ def _build_autocomplete_system_prompt(app_name: str, window_title: str) -> str:
class _KBResult:
"""Container for pre-computed KB filesystem results."""
+
__slots__ = ("files", "ls_ai_msg", "ls_tool_msg")
def __init__(
@@ -171,13 +173,16 @@ async def precompute_kb_filesystem(
return _KBResult()
doc_paths = [
- p for p, v in new_files.items()
+ p
+ for p, v in new_files.items()
if p.startswith("/documents/") and v is not None
]
tool_call_id = f"auto_ls_{uuid.uuid4().hex[:12]}"
ai_msg = AIMessage(
content="",
- tool_calls=[{"name": "ls", "args": {"path": "/documents"}, "id": tool_call_id}],
+ tool_calls=[
+ {"name": "ls", "args": {"path": "/documents"}, "id": tool_call_id}
+ ],
)
tool_msg = ToolMessage(
content=str(doc_paths) if doc_paths else "No documents found.",
@@ -186,7 +191,9 @@ async def precompute_kb_filesystem(
return _KBResult(files=new_files, ls_ai_msg=ai_msg, ls_tool_msg=tool_msg)
except Exception:
- logger.warning("KB pre-computation failed, proceeding without KB", exc_info=True)
+ logger.warning(
+ "KB pre-computation failed, proceeding without KB", exc_info=True
+ )
return _KBResult()
@@ -320,7 +327,9 @@ async def stream_autocomplete_agent(
)
try:
- async for event in agent.astream_events(input_data, config=config, version="v2"):
+ async for event in agent.astream_events(
+ input_data, config=config, version="v2"
+ ):
event_type = event.get("event", "")
if event_type == "on_chat_model_stream":
@@ -338,7 +347,9 @@ async def stream_autocomplete_agent(
yield step_event
current_text_id = streaming_service.generate_text_id()
yield streaming_service.format_text_start(current_text_id)
- yield streaming_service.format_text_delta(current_text_id, content)
+ yield streaming_service.format_text_delta(
+ current_text_id, content
+ )
elif event_type == "on_tool_start":
active_tool_depth += 1
@@ -425,5 +436,7 @@ def _describe_tool_call(tool_name: str, tool_input: Any) -> tuple[str, list[str]
pat = inp.get("pattern", "")
path = inp.get("path", "")
display_pat = pat[:60] + ("…" if len(pat) > 60 else "")
- return "Searching content", [f'"{display_pat}"' + (f" in {path}" if path else "")]
+ return "Searching content", [
+ f'"{display_pat}"' + (f" in {path}" if path else "")
+ ]
return f"Using {tool_name}", []
diff --git a/surfsense_backend/app/services/vision_autocomplete_service.py b/surfsense_backend/app/services/vision_autocomplete_service.py
index 2c2cd65d2..c28962b31 100644
--- a/surfsense_backend/app/services/vision_autocomplete_service.py
+++ b/surfsense_backend/app/services/vision_autocomplete_service.py
@@ -98,7 +98,9 @@ async def stream_vision_autocomplete(
step_id=PREP_STEP_ID,
title="Searching knowledge base",
status="complete",
- items=[f"Found {doc_count} document{'s' if doc_count != 1 else ''}"] if kb_query else ["Skipped"],
+ items=[f"Found {doc_count} document{'s' if doc_count != 1 else ''}"]
+ if kb_query
+ else ["Skipped"],
)
# Build agent input with pre-computed KB as initial state
@@ -116,24 +118,33 @@ async def stream_vision_autocomplete(
"for the active text area based on what you see."
)
- user_message = HumanMessage(content=[
- {"type": "text", "text": instruction},
- {"type": "image_url", "image_url": {"url": screenshot_data_url}},
- ])
+ user_message = HumanMessage(
+ content=[
+ {"type": "text", "text": instruction},
+ {"type": "image_url", "image_url": {"url": screenshot_data_url}},
+ ]
+ )
input_data: dict = {"messages": [user_message]}
if has_kb:
input_data["files"] = kb.files
input_data["messages"] = [kb.ls_ai_msg, kb.ls_tool_msg, user_message]
- logger.info("Autocomplete: injected %d KB files into agent initial state", doc_count)
+ logger.info(
+ "Autocomplete: injected %d KB files into agent initial state", doc_count
+ )
else:
- logger.info("Autocomplete: no KB documents found, proceeding with screenshot only")
+ logger.info(
+ "Autocomplete: no KB documents found, proceeding with screenshot only"
+ )
# Stream the agent (message_start already sent above)
try:
async for sse in stream_autocomplete_agent(
- agent, input_data, streaming, emit_message_start=False,
+ agent,
+ input_data,
+ streaming,
+ emit_message_start=False,
):
yield sse
except Exception as e:
diff --git a/surfsense_web/app/dashboard/[search_space_id]/user-settings/components/DesktopContent.tsx b/surfsense_web/app/dashboard/[search_space_id]/user-settings/components/DesktopContent.tsx
index 07b746a19..a2f9da0f8 100644
--- a/surfsense_web/app/dashboard/[search_space_id]/user-settings/components/DesktopContent.tsx
+++ b/surfsense_web/app/dashboard/[search_space_id]/user-settings/components/DesktopContent.tsx
@@ -3,10 +3,7 @@
import { Clipboard, Sparkles } from "lucide-react";
import { useCallback, useEffect, useState } from "react";
import { toast } from "sonner";
-import {
- DEFAULT_SHORTCUTS,
- ShortcutRecorder,
-} from "@/components/desktop/shortcut-recorder";
+import { DEFAULT_SHORTCUTS, ShortcutRecorder } from "@/components/desktop/shortcut-recorder";
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
import { Label } from "@/components/ui/label";
import { Spinner } from "@/components/ui/spinner";
@@ -29,22 +26,23 @@ export function DesktopContent() {
let mounted = true;
- Promise.all([
- api.getAutocompleteEnabled(),
- api.getShortcuts?.() ?? Promise.resolve(null),
- ]).then(([autoEnabled, config]) => {
- if (!mounted) return;
- setEnabled(autoEnabled);
- if (config) setShortcuts(config);
- setLoading(false);
- setShortcutsLoaded(true);
- }).catch(() => {
- if (!mounted) return;
- setLoading(false);
- setShortcutsLoaded(true);
- });
+ Promise.all([api.getAutocompleteEnabled(), api.getShortcuts?.() ?? Promise.resolve(null)])
+ .then(([autoEnabled, config]) => {
+ if (!mounted) return;
+ setEnabled(autoEnabled);
+ if (config) setShortcuts(config);
+ setLoading(false);
+ setShortcutsLoaded(true);
+ })
+ .catch(() => {
+ if (!mounted) return;
+ setLoading(false);
+ setShortcutsLoaded(true);
+ });
- return () => { mounted = false; };
+ return () => {
+ mounted = false;
+ };
}, [api]);
if (!api) {
diff --git a/surfsense_web/app/dashboard/layout.tsx b/surfsense_web/app/dashboard/layout.tsx
index 25bea5467..1f5481b15 100644
--- a/surfsense_web/app/dashboard/layout.tsx
+++ b/surfsense_web/app/dashboard/layout.tsx
@@ -3,7 +3,7 @@
import { useEffect, useState } from "react";
import { USER_QUERY_KEY } from "@/atoms/user/user-query.atoms";
import { useGlobalLoadingEffect } from "@/hooks/use-global-loading";
-import { getBearerToken, ensureTokensFromElectron, redirectToLogin } from "@/lib/auth-utils";
+import { ensureTokensFromElectron, getBearerToken, redirectToLogin } from "@/lib/auth-utils";
import { queryClient } from "@/lib/query-client/client";
interface DashboardLayoutProps {
diff --git a/surfsense_web/app/desktop/login/page.tsx b/surfsense_web/app/desktop/login/page.tsx
index 529577b59..c81e284ba 100644
--- a/surfsense_web/app/desktop/login/page.tsx
+++ b/surfsense_web/app/desktop/login/page.tsx
@@ -2,30 +2,15 @@
import { IconBrandGoogleFilled } from "@tabler/icons-react";
import { useAtom } from "jotai";
-import {
- Eye,
- EyeOff,
- Keyboard,
- Clipboard,
- Sparkles,
-} from "lucide-react";
+import { Clipboard, Eye, EyeOff, Keyboard, Sparkles } from "lucide-react";
import Image from "next/image";
import { useRouter } from "next/navigation";
import { useCallback, useEffect, useState } from "react";
import { toast } from "sonner";
import { loginMutationAtom } from "@/atoms/auth/auth-mutation.atoms";
-import {
- DEFAULT_SHORTCUTS,
- ShortcutRecorder,
-} from "@/components/desktop/shortcut-recorder";
+import { DEFAULT_SHORTCUTS, ShortcutRecorder } from "@/components/desktop/shortcut-recorder";
import { Button } from "@/components/ui/button";
-import {
- Card,
- CardContent,
- CardDescription,
- CardHeader,
- CardTitle,
-} from "@/components/ui/card";
+import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Separator } from "@/components/ui/separator";
@@ -38,8 +23,7 @@ const isGoogleAuth = AUTH_TYPE === "GOOGLE";
export default function DesktopLoginPage() {
const router = useRouter();
const api = useElectronAPI();
- const [{ mutateAsync: login, isPending: isLoggingIn }] =
- useAtom(loginMutationAtom);
+ const [{ mutateAsync: login, isPending: isLoggingIn }] = useAtom(loginMutationAtom);
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
@@ -54,10 +38,13 @@ export default function DesktopLoginPage() {
setShortcutsLoaded(true);
return;
}
- api.getShortcuts().then((config) => {
- if (config) setShortcuts(config);
- setShortcutsLoaded(true);
- }).catch(() => setShortcutsLoaded(true));
+ api
+ .getShortcuts()
+ .then((config) => {
+ if (config) setShortcuts(config);
+ setShortcutsLoaded(true);
+ })
+ .catch(() => setShortcutsLoaded(true));
}, [api]);
const updateShortcut = useCallback(
@@ -118,8 +105,7 @@ export default function DesktopLoginPage() {
@@ -135,9 +121,7 @@ export default function DesktopLoginPage() {
priority
/>
Welcome to SurfSense Desktop App
-
- Configure your shortcuts, then sign in to get started.
-
+ Configure your shortcuts, then sign in to get started.
@@ -181,11 +165,7 @@ export default function DesktopLoginPage() {
{/* ---- Auth Section (second) ---- */}
{isGoogleAuth ? (
-
diff --git a/surfsense_web/app/desktop/permissions/page.tsx b/surfsense_web/app/desktop/permissions/page.tsx
index b636fcd7c..a2fadc8ff 100644
--- a/surfsense_web/app/desktop/permissions/page.tsx
+++ b/surfsense_web/app/desktop/permissions/page.tsx
@@ -80,7 +80,9 @@ export default function DesktopPermissionsPage() {
poll();
interval = setInterval(poll, 2000);
- return () => { if (interval) clearInterval(interval); };
+ return () => {
+ if (interval) clearInterval(interval);
+ };
}, [api]);
if (!api) {
@@ -204,6 +206,7 @@ export default function DesktopPermissionsPage() {
Grant permissions to continue
diff --git a/surfsense_web/app/desktop/suggestion/page.tsx b/surfsense_web/app/desktop/suggestion/page.tsx
index 587bee9db..8d9095320 100644
--- a/surfsense_web/app/desktop/suggestion/page.tsx
+++ b/surfsense_web/app/desktop/suggestion/page.tsx
@@ -2,7 +2,7 @@
import { useCallback, useEffect, useRef, useState } from "react";
import { useElectronAPI } from "@/hooks/use-platform";
-import { getBearerToken, ensureTokensFromElectron } from "@/lib/auth-utils";
+import { ensureTokensFromElectron, getBearerToken } from "@/lib/auth-utils";
type SSEEvent =
| { type: "text-delta"; id: string; delta: string }
@@ -48,9 +48,20 @@ const AUTO_DISMISS_MS = 3000;
function StepIcon({ status }: { status: string }) {
if (status === "complete") {
return (
-