From 57b066823975b7c331dd77bee0ae0ac2da280af6 Mon Sep 17 00:00:00 2001
From: puzhen <1303385763@qq.com>
Date: Tue, 21 Oct 2025 18:32:20 +0100
Subject: [PATCH 1/6] fix:multi turn files
---
backend/app/service/chat_service.py | 45 +++++++++++++++--------------
1 file changed, 23 insertions(+), 22 deletions(-)
diff --git a/backend/app/service/chat_service.py b/backend/app/service/chat_service.py
index a75e0d295..060917c31 100644
--- a/backend/app/service/chat_service.py
+++ b/backend/app/service/chat_service.py
@@ -182,7 +182,7 @@ def build_conversation_context(task_lock: TaskLock, header: str = "=== CONVERSAT
Formatted context string with task history and files listed once at the end
"""
context = ""
- working_directory = None
+ working_directories = set() # Collect all unique working directories
if task_lock.conversation_history:
context = f"{header}\n"
@@ -193,34 +193,35 @@ def build_conversation_context(task_lock: TaskLock, header: str = "=== CONVERSAT
# Format without file listing
formatted_context = format_task_context(entry['content'], skip_files=True)
context += formatted_context + "\n\n"
- # Remember the working directory from the last task
+ # Collect all working directories from all tasks
if entry['content'].get('working_directory'):
- working_directory = entry['content']['working_directory']
+ working_directories.add(entry['content']['working_directory'])
else:
context += entry['content'] + "\n"
elif entry['role'] == 'assistant':
context += f"Assistant: {entry['content']}\n\n"
- # Add all generated files at the end, only once
- if working_directory:
- try:
- if os.path.exists(working_directory):
- generated_files = []
- for root, dirs, files in os.walk(working_directory):
- dirs[:] = [d for d in dirs if not d.startswith('.') and d not in ['node_modules', '__pycache__', 'venv']]
- for file in files:
- if not file.startswith('.') and not file.endswith(('.pyc', '.tmp')):
- file_path = os.path.join(root, file)
- absolute_path = os.path.abspath(file_path)
- generated_files.append(absolute_path)
+ # Add all generated files from all working directories at the end, only once
+ if working_directories:
+ all_generated_files = set() # Use set to avoid duplicates
+ for working_directory in working_directories:
+ try:
+ if os.path.exists(working_directory):
+ for root, dirs, files in os.walk(working_directory):
+ dirs[:] = [d for d in dirs if not d.startswith('.') and d not in ['node_modules', '__pycache__', 'venv']]
+ for file in files:
+ if not file.startswith('.') and not file.endswith(('.pyc', '.tmp')):
+ file_path = os.path.join(root, file)
+ absolute_path = os.path.abspath(file_path)
+ all_generated_files.add(absolute_path)
+ except Exception as e:
+ logger.warning(f"Failed to collect generated files from {working_directory}: {e}")
- if generated_files:
- context += "Generated Files from Previous Tasks:\n"
- for file_path in sorted(generated_files):
- context += f" - {file_path}\n"
- context += "\n"
- except Exception as e:
- logger.warning(f"Failed to collect generated files: {e}")
+ if all_generated_files:
+ context += "Generated Files from Previous Tasks:\n"
+ for file_path in sorted(all_generated_files):
+ context += f" - {file_path}\n"
+ context += "\n"
context += "\n"
From a47357e8e6012129817c4d34a4d26134f4452289 Mon Sep 17 00:00:00 2001
From: puzhen <1303385763@qq.com>
Date: Tue, 21 Oct 2025 18:42:24 +0100
Subject: [PATCH 2/6] update
---
backend/app/service/chat_service.py | 3 ---
1 file changed, 3 deletions(-)
diff --git a/backend/app/service/chat_service.py b/backend/app/service/chat_service.py
index 060917c31..18bab0aca 100644
--- a/backend/app/service/chat_service.py
+++ b/backend/app/service/chat_service.py
@@ -190,10 +190,8 @@ def build_conversation_context(task_lock: TaskLock, header: str = "=== CONVERSAT
for entry in task_lock.conversation_history:
if entry['role'] == 'task_result':
if isinstance(entry['content'], dict):
- # Format without file listing
formatted_context = format_task_context(entry['content'], skip_files=True)
context += formatted_context + "\n\n"
- # Collect all working directories from all tasks
if entry['content'].get('working_directory'):
working_directories.add(entry['content']['working_directory'])
else:
@@ -201,7 +199,6 @@ def build_conversation_context(task_lock: TaskLock, header: str = "=== CONVERSAT
elif entry['role'] == 'assistant':
context += f"Assistant: {entry['content']}\n\n"
- # Add all generated files from all working directories at the end, only once
if working_directories:
all_generated_files = set() # Use set to avoid duplicates
for working_directory in working_directories:
From ccb7c91a23c271dfdd7708000b4b2219fb615105 Mon Sep 17 00:00:00 2001
From: puzhen <1303385763@qq.com>
Date: Tue, 21 Oct 2025 19:21:17 +0100
Subject: [PATCH 3/6] fix: ui
---
.gitignore | 7 +++++
src/components/TopBar/index.tsx | 26 +++++++++++++++++-
src/i18n/locales/en-us/layout.json | 1 +
src/i18n/locales/zh-Hans/layout.json | 1 +
src/i18n/locales/zh-Hant/layout.json | 1 +
src/pages/History.tsx | 6 ++--
utils/__pycache__/__init__.cpython-310.pyc | Bin 155 -> 0 bytes
.../traceroot_wrapper.cpython-310.pyc | Bin 2359 -> 0 bytes
8 files changed, 39 insertions(+), 3 deletions(-)
delete mode 100644 utils/__pycache__/__init__.cpython-310.pyc
delete mode 100644 utils/__pycache__/traceroot_wrapper.cpython-310.pyc
diff --git a/.gitignore b/.gitignore
index 38dc4b280..c9280a9e2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -47,3 +47,10 @@ public/
# Testing
coverage/
.traceroot-config.yaml
+
+# Python
+__pycache__/
+*.py[cod]
+*$py.class
+*.so
+.Python
diff --git a/src/components/TopBar/index.tsx b/src/components/TopBar/index.tsx
index e20aea664..4768d3aa3 100644
--- a/src/components/TopBar/index.tsx
+++ b/src/components/TopBar/index.tsx
@@ -224,6 +224,19 @@ function HeaderWin() {
+ {location.pathname === "/history" && (
+
F>6tKR#3io;Z!@;Kwr9gyiCgEy za|9LTGdikVkb@a+FB9&p60a(3Q9T1RQ5wx&5cvj9mgBkZ4X=iC2lL*%sJAJvh?{N3 zXL$9z^0 z8f) VX nWmY4EQ4MT?$kTcH6PUb-IVv?QVE#o0KEDfI)^!w(FQZ= z9L14kI{!e=*k8;xDf^olCN=++H(~30^v%F*LFj=A6-12FPDe;>B4mimlLyBPT22R4 zmH`pYDDaC8)}D*ZeT`;*4`#Xrh6|p|f+Q3zz+TABy}C)UrAcK2kaE`)T^+>*= McG8Ws=jQN`9APShER`*7w);sa zm*D9C0*KDqMPSr0gkh8J -LprjCY&^0va^MtWB*@?~5*FFX1IgW2C)s|QOlW(&FOuuL z;gKqcGx&1-UZBD(>WZdvH{A7mm?q<4ERIAxC3k@VK?hcwWo;whp;qZK U2qK3#m&B>o!6%bm1v+Ll74@sU6fEbt0G^U0Bqh}uNcyjo69thlYhorUg2 z#6gnc#{>fn9}`1mP%QV-G%jky%v448sy@P9meeSLSbPEJy_!F{++@*)R7sE^w(rBe zeg9L}=IQ7>Dd3-CG}L|Qs-y}<-hr4oMq?gIKdVBiud_MRDmk Date: Tue, 21 Oct 2025 20:07:14 +0100 Subject: [PATCH 4/6] chore: clean cache --- electron/main/install-deps.ts | 36 +++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/electron/main/install-deps.ts b/electron/main/install-deps.ts index c475ab94b..257fca38e 100644 --- a/electron/main/install-deps.ts +++ b/electron/main/install-deps.ts @@ -6,6 +6,7 @@ import fs from 'node:fs' import { getBackendPath, getBinaryPath, getCachePath, getVenvPath, cleanupOldVenvs, isBinaryExists, runInstallScript } from './utils/process' import { spawn } from 'child_process' import { safeMainWindowSend } from './utils/safeWebContentsSend' +import os from 'node:os' const userData = app.getPath('userData'); const versionFile = path.join(userData, 'version.txt'); @@ -57,6 +58,13 @@ Promise => { return new Promise(async (resolve, reject) => { try { + // Clean up cache in production environment BEFORE any checks + // This ensures users always get fresh dependencies in production + if (app.isPackaged) { + log.info('[CACHE CLEANUP] Production environment detected, cleaning cache before dependency check...'); + cleanupCacheInProduction(); + } + const versionExists:boolean = checkInstallOperations.getSavedVersion(); // Check if command tools are installed @@ -280,6 +288,34 @@ class InstallLogs { } } +/** + * Clean up cache directory + * This ensures users get fresh dependencies + * Note: Only call this in production environment (caller should check app.isPackaged) + */ +function cleanupCacheInProduction(): void { + try { + const cacheBaseDir = path.join(os.homedir(), '.eigent', 'cache'); + + if (!fs.existsSync(cacheBaseDir)) { + log.info('[CACHE CLEANUP] Cache directory does not exist, nothing to clean'); + return; + } + + log.info('[CACHE CLEANUP] Cleaning cache directory:', cacheBaseDir); + + fs.rmSync(cacheBaseDir, { recursive: true, force: true }); + + log.info('[CACHE CLEANUP] Cache directory cleaned successfully'); + + fs.mkdirSync(cacheBaseDir, { recursive: true }); + log.info('[CACHE CLEANUP] Empty cache directory recreated'); + + } catch (error) { + log.error('[CACHE CLEANUP] Failed to clean cache directory:', error); + } +} + const runInstall = (extraArgs: string[], version: string) => { const installLogs = new InstallLogs(extraArgs, version); return new Promise ((resolveInner, rejectInner) => { From 4d07e90df2c3176c4c48a5a93a2e033a55b8eda2 Mon Sep 17 00:00:00 2001 From: puzhen <1303385763@qq.com> Date: Wed, 22 Oct 2025 12:56:38 +0100 Subject: [PATCH 5/6] fix: display bugs --- backend/app/utils/listen/toolkit_listen.py | 18 ++++++++++++------ src/components/ChatBox/TaskCard.tsx | 20 ++++++++++++++++++-- src/components/ChatBox/UserQueryGroup.tsx | 9 +++++---- src/components/TopBar/index.tsx | 8 ++++---- src/pages/Home.tsx | 8 ++++---- src/store/chatStore.ts | 3 ++- 6 files changed, 45 insertions(+), 21 deletions(-) diff --git a/backend/app/utils/listen/toolkit_listen.py b/backend/app/utils/listen/toolkit_listen.py index fb5b9bf5f..7e98713ba 100644 --- a/backend/app/utils/listen/toolkit_listen.py +++ b/backend/app/utils/listen/toolkit_listen.py @@ -285,11 +285,17 @@ def auto_listen_toolkit(base_toolkit_class: Type[T]) -> Callable[[Type[T]], Type for method_name, base_method in base_methods.items(): if method_name in cls.__dict__: continue - + sig = signature(base_method) - + def create_wrapper(method_name: str, base_method: Callable) -> Callable: - if iscoroutinefunction(base_method): + # Unwrap decorators to check the actual function + unwrapped_method = base_method + while hasattr(unwrapped_method, '__wrapped__'): + unwrapped_method = unwrapped_method.__wrapped__ + + # Check if the unwrapped method is a coroutine function + if iscoroutinefunction(unwrapped_method): async def async_method_wrapper(self, *args, **kwargs): return await getattr(super(cls, self), method_name)(*args, **kwargs) async_method_wrapper.__name__ = method_name @@ -301,12 +307,12 @@ def auto_listen_toolkit(base_toolkit_class: Type[T]) -> Callable[[Type[T]], Type sync_method_wrapper.__name__ = method_name sync_method_wrapper.__signature__ = sig return sync_method_wrapper - + wrapper = create_wrapper(method_name, base_method) decorated_method = listen_toolkit(base_method)(wrapper) - + setattr(cls, method_name, decorated_method) return cls - + return class_decorator diff --git a/src/components/ChatBox/TaskCard.tsx b/src/components/ChatBox/TaskCard.tsx index 26d7663f7..03a85f8a1 100644 --- a/src/components/ChatBox/TaskCard.tsx +++ b/src/components/ChatBox/TaskCard.tsx @@ -35,6 +35,7 @@ interface TaskCardProps { onUpdateTask: (taskIndex: number, content: string) => void; onDeleteTask: (taskIndex: number) => void; clickable?: boolean; + chatId?: string; } export function TaskCard({ @@ -47,14 +48,15 @@ export function TaskCard({ onUpdateTask, onDeleteTask, clickable = true, + chatId, }: TaskCardProps) { const { t } = useTranslation(); const [isExpanded, setIsExpanded] = useState(true); const contentRef = useRef (null); const [contentHeight, setContentHeight] = useState ("auto"); - //Get Chatstore for the active project's task - const { chatStore } = useChatStoreAdapter(); + //Get Chatstore and ProjectStore for the active project's task + const { chatStore, projectStore } = useChatStoreAdapter(); if (!chatStore) { return Loading...; } @@ -329,6 +331,20 @@ export function TaskCard({{ if (task.agent) { + // Switch to the chatStore that owns this task card (for multi-turn conversations) + if (chatId && projectStore.activeProjectId) { + const activeChatStore = projectStore.getActiveChatStore(); + const currentChatId = activeChatStore ? Object.keys(projectStore.projects[projectStore.activeProjectId].chatStores).find( + id => projectStore.projects[projectStore.activeProjectId].chatStores[id] === activeChatStore + ) : null; + + // Only switch if this is a different chat + if (currentChatId !== chatId) { + projectStore.setActiveChatStore(projectStore.activeProjectId, chatId); + } + } + + // Set the active workspace and agent chatStore.setActiveWorkSpace( chatStore.activeTaskId as string, "workflow" diff --git a/src/components/ChatBox/UserQueryGroup.tsx b/src/components/ChatBox/UserQueryGroup.tsx index 7776bba68..a90bc3a35 100644 --- a/src/components/ChatBox/UserQueryGroup.tsx +++ b/src/components/ChatBox/UserQueryGroup.tsx @@ -38,14 +38,14 @@ export const UserQueryGroup: React.FC= ({ // Show task if this query group has a task message OR if it's the most recent user query during splitting // During splitting phase (no to_sub_tasks yet), show task for the most recent query only - const isLastUserQuery = !queryGroup.taskMessage && - activeTaskId && + const isLastUserQuery = !queryGroup.taskMessage && + activeTaskId && chatState.tasks[activeTaskId] && - queryGroup.userMessage && + queryGroup.userMessage && queryGroup.userMessage.id === chatState.tasks[activeTaskId].messages.filter((m: any) => m.role === 'user').pop()?.id && // Only show during active phases (not finished) chatState.tasks[activeTaskId].status !== 'finished'; - + const task = (queryGroup.taskMessage || isLastUserQuery) && activeTaskId ? chatState.tasks[activeTaskId] : null; // Set up intersection observer for this query group @@ -185,6 +185,7 @@ export const UserQueryGroup: React.FC = ({ > 0 || - chatStore.tasks[chatStore.activeTaskId as string].hasMessages || - chatStore.tasks[chatStore.activeTaskId as string].status !== 'pending' + (chatStore.tasks[chatStore.activeTaskId as string]?.messages?.length || 0) > 0 || + chatStore.tasks[chatStore.activeTaskId as string]?.hasMessages || + chatStore.tasks[chatStore.activeTaskId as string]?.status !== 'pending' ) && (