From d75df5a4b4b052679f55402d5e5646e47803cf38 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Wed, 29 Jul 2026 13:38:20 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=96=B0=E5=AF=B9=E8=AF=9D?= =?UTF-8?q?=E7=9B=AE=E5=BD=95=E9=80=89=E6=8B=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 4 ++++ PROGRESS.md | 3 ++- core/__init__.py | 2 +- web/static/dev.html | 1 + web/static/js/chat.js | 46 ++++++++++++++++++++++++++++++++----------- 5 files changed, 42 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 251c952..40b3f18 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,10 @@ > 所以不是每个版本号都有条目。条目格式 `## <版本> — <日期>`,新条目加在最上面。 > 工程口径的完整记录见 `PROGRESS.md` / git log。 +## 0.60.5 — 2026-07-29 + +- 新对话仍会明确指定文件保存位置,同时目录选择更快捷:最近使用的目录会自动选中并置顶,目录较多时可以直接搜索。 + ## 0.60.4 — 2026-07-29 - 管理后台的工具失败信息现在会区分近期跨任务故障、单任务反复失败、按设计拦截的质量检查和已安静历史,并显示工具调用参数自动抢救率,排查时更容易判断问题是否仍在发生。 diff --git a/PROGRESS.md b/PROGRESS.md index 069c39c..a2120fa 100644 --- a/PROGRESS.md +++ b/PROGRESS.md @@ -2,7 +2,7 @@ > 配合 `DESIGN.md`。本文件只记 phase 状态、决策偏差、文件量、下一步。每条 1-2 句:做了啥 + 关键判断;细节查 `git log` / `git diff` / `DESIGN §7.9`。 -最后更新:2026-07-29(工具失败信号分层 + wire 健康观测,bump 0.60.4) +最后更新:2026-07-29(新对话目录选择提速,bump 0.60.5) --- @@ -23,6 +23,7 @@ ### 2026-07-29 +- **07-29 / 0.60.5 / 新对话目录选择提速**:保留首发前明确选择 working_dir 的归档约束;本机最近实际使用目录在候选中自动选中、置顶并标记,目录达到 6 个时按需显示搜索框,目录较少时维持原有简洁界面;同步精简“文件保存位置”文案。纯前端改动,无 API、schema、migration 或目录绑定语义变化;相关静态资源与无 DB 路由 18 项测试通过。 - **07-29 / 0.60.4 / 工具失败信号分层 + run_python 同错保护 + wire 健康观测**:管理端保留近7天低阈值全量事实,默认把近24h活跃数据拆成跨任务系统性故障、单任务反复失败、按设计非零退出的质量门,已归零记录沉入历史区;空输出 shell 关联最近 assistant tool_call 补 `search/no match` / `dependency probe` 等稳定类别。RepeatGuard 仅将 `run_python` traceback+非零退出纳入同错 streak,普通 shell exit 1 保持原语义。新增只读 `/v1/admin/tool-wire-health`,按 model_profile+tool 对既有 `tool_salvaged/tool_malformed` 计算24h/7d抢救率,无新表、migration、索引或执行策略变化。全量387测试通过(17项按环境跳过)。 ### 2026-07-28 diff --git a/core/__init__.py b/core/__init__.py index 11db414..93fde70 100644 --- a/core/__init__.py +++ b/core/__init__.py @@ -1,3 +1,3 @@ # zcbot 版本号单一事实源:web/app.py 的 FastAPI version、/healthz 返回、前端展示都引这里。 # 改版本只动这一行。 -__version__ = "0.60.4" +__version__ = "0.60.5" diff --git a/web/static/dev.html b/web/static/dev.html index c3facb3..353812f 100644 --- a/web/static/dev.html +++ b/web/static/dev.html @@ -772,6 +772,7 @@ .new-chat-start h2 { margin: 0 0 8px; font-size: 20px; } .new-chat-start .lead { margin: 0 0 24px; color: var(--muted); font-size: 13px; } .new-chat-start label { display:block; margin-bottom:7px; font-size:13px; font-weight:600; } + .new-chat-dir-search { width:100%; margin-bottom:8px; } .new-chat-dir-row { display:flex; gap:8px; align-items:center; } .new-chat-dir-row select, .new-chat-dir-row input { min-width:0; flex:1; } .new-chat-new-dir { display:none; margin-top:10px; } diff --git a/web/static/js/chat.js b/web/static/js/chat.js index 7a67443..a3ecdf8 100644 --- a/web/static/js/chat.js +++ b/web/static/js/chat.js @@ -401,32 +401,49 @@ function draftWorkingDir() { function syncDraftAction() { const ready = !!draftWorkingDir(); setActionMode(ready ? "idle" : "loading"); - $("chat-hint").textContent = ready ? "输入消息开始对话" : "请先选择或新建工作目录"; + $("chat-hint").textContent = ready ? "输入消息开始对话" : "请选择文件保存位置"; const note = $("new-chat-dir-note"); if (note) { note.textContent = ready - ? `本次对话生成的文件将保存到「${draftWorkingDir()}」` - : "工作目录用于保存本次对话生成的文件,同一目录可以包含多段对话。"; + ? `文件将保存到「${draftWorkingDir()}」` + : "可选择已有目录,也可以新建目录。"; } } function renderDraftFolderOptions() { const sel = $("new-chat-dir-select"); if (!sel) return; - const folders = state.folders || []; + const search = $("new-chat-dir-search"); + const query = search ? search.value.trim().toLocaleLowerCase() : ""; + const recent = localStorage.getItem(LS_RECENT_WORKING_DIR) || ""; + const folders = [...(state.folders || [])]; + // API 已按最近使用时间排序;再把本机刚选过的目录固定到第一项,避免 + // task 列表更新时间尚未刷新时“最近目录”在新对话入口里跳位。 + folders.sort((a, b) => Number(b.name === recent) - Number(a.name === recent)); + const visible = query + ? folders.filter(f => f.name.toLocaleLowerCase().includes(query)) + : folders; const selected = state.draftNewDirMode ? "" : draftWorkingDir(); - sel.innerHTML = [ - ``, - ...folders.map(f => { + const options = visible.map(f => { const tag = f.n_tasks ? `${f.n_tasks} 个任务` : "空目录"; - return ``; - }), - ].join(""); + const recentTag = f.name === recent ? "最近使用 · " : ""; + return ``; + }); + // 搜索词不匹配当前选择时仍保留它,避免仅仅输入搜索词就悄悄清掉目录。 + if (selected && !visible.some(f => f.name === selected)) { + options.unshift(``); + } + if (!visible.length && query) { + options.push(``); + } + sel.innerHTML = [``, ...options].join(""); if (folders.some(f => f.name === selected)) sel.value = selected; + if (search) search.hidden = folders.length < 6; } function bindDraftControls() { const sel = $("new-chat-dir-select"); + const search = $("new-chat-dir-search"); const add = $("new-chat-dir-add"); const box = $("new-chat-new-dir"); const input = $("new-chat-dir-input"); @@ -444,6 +461,9 @@ function bindDraftControls() { } syncDraftAction(); }; + if (search) { + search.oninput = () => renderDraftFolderOptions(); + } add.onclick = () => { state.draftNewDirMode = true; state.draftWorkingDir = ""; @@ -478,8 +498,10 @@ export async function showNewConversationDraft() { $("chat-stream").innerHTML = `

开始一段新对话

-

先确定文件保存位置,然后直接告诉助手你想做什么。

- +

选择文件保存位置,然后告诉助手你想做什么。

+ +