diff --git a/apps/x/apps/renderer/src/components/settings-dialog.tsx b/apps/x/apps/renderer/src/components/settings-dialog.tsx
index 657b66b9..248c6cc6 100644
--- a/apps/x/apps/renderer/src/components/settings-dialog.tsx
+++ b/apps/x/apps/renderer/src/components/settings-dialog.tsx
@@ -364,6 +364,11 @@ function ModelSettings({ dialogOpen, rowboatConnected = false }: { dialogOpen: b
const [testState, setTestState] = useState<{ status: "idle" | "testing" | "success" | "error"; error?: string }>({ status: "idle" })
const [configLoading, setConfigLoading] = useState(true)
const [showMoreProviders, setShowMoreProviders] = useState(false)
+ // "Defer background tasks while a chat is running" — a top-level
+ // models.json flag. deferExplicit tracks whether the user (or the Ollama
+ // auto-enable) has ever set it, so we only auto-enable once.
+ const [deferBackgroundTasks, setDeferBackgroundTasks] = useState(false)
+ const [deferExplicit, setDeferExplicit] = useState(false)
const activeConfig = providerConfigs[provider]
const showApiKey = provider === "openai" || provider === "anthropic" || provider === "google" || provider === "openrouter" || provider === "aigateway" || provider === "openai-compatible"
@@ -406,6 +411,8 @@ function ModelSettings({ dialogOpen, rowboatConnected = false }: { dialogOpen: b
path: "config/models.json",
})
const parsed = JSON.parse(result.data)
+ setDeferBackgroundTasks(parsed?.deferBackgroundTasks === true)
+ setDeferExplicit(typeof parsed?.deferBackgroundTasks === "boolean")
if (parsed?.provider?.flavor && parsed?.model) {
const flavor = parsed.provider.flavor as LlmProviderFlavor
setProvider(flavor)
@@ -462,6 +469,17 @@ function ModelSettings({ dialogOpen, rowboatConnected = false }: { dialogOpen: b
loadCurrentConfig()
}, [dialogOpen])
+ const handleDeferToggle = useCallback(async (value: boolean) => {
+ setDeferBackgroundTasks(value)
+ setDeferExplicit(true)
+ try {
+ await window.ipc.invoke("models:updateConfig", { deferBackgroundTasks: value })
+ window.dispatchEvent(new Event("models-config-changed"))
+ } catch {
+ toast.error("Failed to save setting")
+ }
+ }, [])
+
// Load models catalog
useEffect(() => {
if (!dialogOpen) return
@@ -532,6 +550,12 @@ function ModelSettings({ dialogOpen, rowboatConnected = false }: { dialogOpen: b
setDefaultProvider(provider)
setTestState({ status: "success" })
window.dispatchEvent(new Event('models-config-changed'))
+ // Local models compete with background agents for the same hardware:
+ // when the user connects Ollama and has never touched the defer
+ // flag, enable it for them (they can switch it off below).
+ if (provider === "ollama" && !deferExplicit && !deferBackgroundTasks) {
+ void handleDeferToggle(true)
+ }
// Capability probe caveats (local models): saved, but the user should
// know when the model can't do tools or has a too-small context.
const warnings: string[] = result.warnings ?? []
@@ -551,7 +575,7 @@ function ModelSettings({ dialogOpen, rowboatConnected = false }: { dialogOpen: b
setTestState({ status: "error", error: "Connection test failed" })
toast.error("Connection test failed")
}
- }, [canTest, provider, activeConfig, rowboatConnected])
+ }, [canTest, provider, activeConfig, rowboatConnected, deferExplicit, deferBackgroundTasks, handleDeferToggle])
const handleSetDefault = useCallback(async (prov: LlmProviderFlavor) => {
const config = providerConfigs[prov]
@@ -938,6 +962,17 @@ function ModelSettings({ dialogOpen, rowboatConnected = false }: { dialogOpen: b
)}
+ {/* Defer background tasks while chatting */}
+
+
+
Defer background tasks while chatting
+
+ Background agents (knowledge sync, live notes, tasks) wait until your chat finishes. Recommended for local models.
+
+
+
+
+
{/* Test & Save button */}