diff --git a/agent/tools/bash/bash.py b/agent/tools/bash/bash.py index 4fcc724c..dad6fb95 100644 --- a/agent/tools/bash/bash.py +++ b/agent/tools/bash/bash.py @@ -22,7 +22,7 @@ class Bash(BaseTool): """Tool for executing bash commands""" _IS_WIN = sys.platform == "win32" - _PROGRESS_MAX_BYTES = 32 * 1024 + _PROGRESS_MAX_BYTES = 4 * 1024 _PROGRESS_INTERVAL = 0.5 name: str = "bash" @@ -290,8 +290,9 @@ SAFETY: if process.poll() is None: self._kill_process(process) process.wait() + join_deadline = time.monotonic() + 5 for reader in readers: - reader.join() + reader.join(timeout=max(0, join_deadline - time.monotonic())) from types import SimpleNamespace return SimpleNamespace( @@ -302,12 +303,23 @@ SAFETY: def _kill_process(self, process): if self._IS_WIN: - process.kill() + try: + result = subprocess.run( + ["taskkill", "/F", "/T", "/PID", str(process.pid)], + capture_output=True, + timeout=5, + ) + if result.returncode != 0 and process.poll() is None: + process.kill() + except (OSError, subprocess.SubprocessError): + if process.poll() is None: + process.kill() else: try: os.killpg(process.pid, signal.SIGKILL) - except PermissionError: - process.kill() + except (PermissionError, ProcessLookupError): + if process.poll() is None: + process.kill() @staticmethod def _redact_progress(text: str, dotenv_vars: dict) -> str: diff --git a/channel/web/static/js/console.js b/channel/web/static/js/console.js index 31727cbf..825871a9 100644 --- a/channel/web/static/js/console.js +++ b/channel/web/static/js/console.js @@ -188,6 +188,8 @@ const I18N = { delete_session_title: '删除会话', delete_message_confirm: '确认删除这条消息?', delete_message_title: '删除消息', + edit_disabled_reply_active: '正在生成回复,暂时无法编辑。', + delete_disabled_reply_active: '正在生成回复,暂时无法删除。', untitled_session: '新对话', context_cleared: '— 以上内容已从上下文中移除 —', tip_new_chat: '新建对话', @@ -392,6 +394,8 @@ const I18N = { delete_session_title: 'Delete Session', delete_message_confirm: 'Delete this message?', delete_message_title: 'Delete Message', + edit_disabled_reply_active: 'Reply is being generated; editing is temporarily unavailable.', + delete_disabled_reply_active: 'Reply is being generated; deletion is temporarily unavailable.', untitled_session: 'New Chat', context_cleared: '— Context above has been cleared —', tip_new_chat: 'New Chat', @@ -900,11 +904,11 @@ function updateEditButtonsState() { btn.disabled = active; if (btn.classList.contains('edit-msg-btn')) { btn.title = active - ? 'Reply is being generated; editing is temporarily unavailable.' - : (currentLang === 'zh' ? '编辑消息' : 'Edit Message'); + ? t('edit_disabled_reply_active') + : t('edit_message'); } else { btn.title = active - ? 'Reply is being generated; deletion is temporarily unavailable.' + ? t('delete_disabled_reply_active') : t('delete_message_title'); } }); @@ -2446,6 +2450,7 @@ function startSSE(requestId, loadingEl, timestamp, titleInfo, replayItems) { // Add tool execution indicator (collapsible) const toolEl = document.createElement('div'); toolEl.className = 'agent-step agent-tool-step tool-streaming'; + toolEl.dataset.progressReceived = 'false'; const argsStr = formatToolArgs(item.arguments || {}); toolEl.innerHTML = `