diff --git a/tests/frontend_chat_queue.test.mjs b/tests/frontend_chat_queue.test.mjs index 1923081..8f89b3e 100644 --- a/tests/frontend_chat_queue.test.mjs +++ b/tests/frontend_chat_queue.test.mjs @@ -66,3 +66,12 @@ test("composer uses one Codex-style action button for stop and queued send", () assert.match(chat, /void dispatchNextQueuedMessage\(ctx\.taskId\)/); assert.match(chat, /removeQueuedMessage\([\s\S]*streamSse\(r\.events_url, run\)/); }); + +test("automatic title polling starts before a long-running response finishes", () => { + const chat = readFileSync(new URL("../web/static/js/chat.js", import.meta.url), "utf8"); + const sendMessage = chat.indexOf("async function sendMessage("); + const poll = chat.indexOf("void pollAutoTitle(taskId)", sendMessage); + const stream = chat.indexOf("streamSse(r.events_url, run)", sendMessage); + assert.ok(poll > sendMessage, "sendMessage should start title polling"); + assert.ok(poll < stream, "title polling should start before waiting on the SSE run"); +}); diff --git a/web/static/js/chat.js b/web/static/js/chat.js index 85b3907..12864e9 100644 --- a/web/static/js/chat.js +++ b/web/static/js/chat.js @@ -3105,6 +3105,11 @@ async function sendMessage(overrideText) { state.streaming = true; syncTaskRowRunIndicator(taskId); if (state.taskId === taskId) setActionMode("streaming"); + // 标题生成与主 run 并行,页面也应立即开始观察结果。若等 SSE 收尾才轮询, + // 长思考会让已经写入 DB 的标题在 UI 中继续显示数分钟“新对话”。 + if (run.autoTitleEligible && state.taskMeta?.auto_title_pending) { + void pollAutoTitle(taskId); + } streamSse(r.events_url, run); } catch (e) { if (e.status === 401) { logout(); return; }