diff --git a/.env.example b/.env.example
index dc00d2e6..4077e9cd 100644
--- a/.env.example
+++ b/.env.example
@@ -42,6 +42,12 @@ ARCHON_DOCS_PORT=3838
# If not set, defaults to localhost, 127.0.0.1, ::1, and the HOST value above
VITE_ALLOWED_HOSTS=
+# Development Tools
+# VITE_SHOW_DEVTOOLS: Show TanStack Query DevTools (for developers only)
+# Set to "true" to enable the DevTools panel in bottom right corner
+# Defaults to "false" for end users
+VITE_SHOW_DEVTOOLS=false
+
# When enabled, PROD mode will proxy ARCHON_SERVER_PORT through ARCHON_UI_PORT. This exposes both the
# Archon UI and API through a single port. This is useful when deploying Archon behind a reverse
# proxy where you want to expose the frontend on a single external domain.
diff --git a/archon-ui-main/package-lock.json b/archon-ui-main/package-lock.json
index c5a0773e..f6570a5e 100644
--- a/archon-ui-main/package-lock.json
+++ b/archon-ui-main/package-lock.json
@@ -12,6 +12,8 @@
"@milkdown/kit": "^7.5.0",
"@milkdown/plugin-history": "^7.5.0",
"@milkdown/preset-commonmark": "^7.5.0",
+ "@tanstack/react-query": "^5.85.8",
+ "@tanstack/react-query-devtools": "^5.85.8",
"@xyflow/react": "^12.3.0",
"clsx": "latest",
"date-fns": "^4.1.0",
@@ -2571,6 +2573,59 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/@tanstack/query-core": {
+ "version": "5.85.7",
+ "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.85.7.tgz",
+ "integrity": "sha512-FLT3EtuTbXBmOrDku4bI80Eivmjn/o/Zc1lVEd/6yzR8UAUSnDwYiwghCZvLqHyGSN5mO35ux1yPGMFYBFRSwA==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/tannerlinsley"
+ }
+ },
+ "node_modules/@tanstack/query-devtools": {
+ "version": "5.84.0",
+ "resolved": "https://registry.npmjs.org/@tanstack/query-devtools/-/query-devtools-5.84.0.tgz",
+ "integrity": "sha512-fbF3n+z1rqhvd9EoGp5knHkv3p5B2Zml1yNRjh7sNXklngYI5RVIWUrUjZ1RIcEoscarUb0+bOvIs5x9dwzOXQ==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/tannerlinsley"
+ }
+ },
+ "node_modules/@tanstack/react-query": {
+ "version": "5.85.8",
+ "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.85.8.tgz",
+ "integrity": "sha512-r3rW55STAO03EJg5mrCVIJvaEK3oeHme5u7QovuRFIKRbEgTzTv2DPdenX46X+x56LsU3ree1N4rzI/+gJ7KEA==",
+ "license": "MIT",
+ "dependencies": {
+ "@tanstack/query-core": "5.85.7"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/tannerlinsley"
+ },
+ "peerDependencies": {
+ "react": "^18 || ^19"
+ }
+ },
+ "node_modules/@tanstack/react-query-devtools": {
+ "version": "5.85.8",
+ "resolved": "https://registry.npmjs.org/@tanstack/react-query-devtools/-/react-query-devtools-5.85.8.tgz",
+ "integrity": "sha512-83SXqRpmVlRMpaj32veez/8ohjY7O4VQIYDqW91b4i9AQjiYgE24FbBfR/SOL8b5MfKhHMZkD+BQSpCh9jY06w==",
+ "license": "MIT",
+ "dependencies": {
+ "@tanstack/query-devtools": "5.84.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/tannerlinsley"
+ },
+ "peerDependencies": {
+ "@tanstack/react-query": "^5.85.8",
+ "react": "^18 || ^19"
+ }
+ },
"node_modules/@testing-library/dom": {
"version": "10.4.0",
"resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.0.tgz",
diff --git a/archon-ui-main/package.json b/archon-ui-main/package.json
index 1f5a91c8..1e9ebed6 100644
--- a/archon-ui-main/package.json
+++ b/archon-ui-main/package.json
@@ -22,6 +22,8 @@
"@milkdown/kit": "^7.5.0",
"@milkdown/plugin-history": "^7.5.0",
"@milkdown/preset-commonmark": "^7.5.0",
+ "@tanstack/react-query": "^5.85.8",
+ "@tanstack/react-query-devtools": "^5.85.8",
"@xyflow/react": "^12.3.0",
"clsx": "latest",
"date-fns": "^4.1.0",
diff --git a/archon-ui-main/src/App.tsx b/archon-ui-main/src/App.tsx
index 427347cb..a47a0461 100644
--- a/archon-ui-main/src/App.tsx
+++ b/archon-ui-main/src/App.tsx
@@ -1,5 +1,7 @@
import { useState, useEffect } from 'react';
import { BrowserRouter as Router, Routes, Route, Navigate } from 'react-router-dom';
+import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
+import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
import { KnowledgeBasePage } from './pages/KnowledgeBasePage';
import { SettingsPage } from './pages/SettingsPage';
import { MCPPage } from './pages/MCPPage';
@@ -15,6 +17,28 @@ import { MigrationBanner } from './components/ui/MigrationBanner';
import { serverHealthService } from './services/serverHealthService';
import { useMigrationStatus } from './hooks/useMigrationStatus';
+// Create a client with optimized settings for our polling use case
+const queryClient = new QueryClient({
+ defaultOptions: {
+ queries: {
+ // Keep data fresh for 2 seconds by default
+ staleTime: 2000,
+ // Cache data for 5 minutes
+ gcTime: 5 * 60 * 1000,
+ // Retry failed requests 3 times
+ retry: 3,
+ // Refetch on window focus
+ refetchOnWindowFocus: true,
+ // Don't refetch on reconnect by default (we handle this manually)
+ refetchOnReconnect: false,
+ },
+ mutations: {
+ // Retry mutations once on failure
+ retry: 1,
+ },
+ },
+});
+
const AppRoutes = () => {
const { projectsEnabled } = useSettings();
@@ -105,12 +129,17 @@ const AppContent = () => {
export function App() {
return (
-