mirror of
https://github.com/bytedance/deer-flow.git
synced 2026-07-28 08:56:13 +00:00
* fix(frontend): resolve stale subagent running state after stop Subtask cards were recreated from task tool calls as in_progress whenever the thread was loading. If a user stopped a run before the task tool returned a ToolMessage, the card could remain running after reload, and could flip back to running when a later user turn started streaming. Derive pending subtask state from the current assistant turn instead of the global thread loading flag. A task now stays in progress only while its own turn is loading or while a matching ToolMessage exists for result parsing; otherwise it is marked failed. Add unit coverage for task ToolMessage matching and for the later-turn loading regression. * fix(frontend): refresh subagent card on terminal status transition - notify React via a ref + post-render effect when a subtask flips to completed/failed, so SubtaskCard updates without relying on an unrelated MessageList re-render - tighten the current-turn heuristic to `groupIndex === lastGroupIndex` (precomputed once) — fixes the rare case where an earlier subagent group in the same turn was kept in_progress, and drops the per-group slice+some scan to O(1) - rename `getPendingSubtaskStatus` → `derivePendingSubtaskStatus` - narrow `toolCall.id` at MessageList call sites; skip task tool calls without an id instead of `id!` - add Playwright e2e covering reload-after-stop showing `Subtask failed`
67 lines
1.7 KiB
TypeScript
67 lines
1.7 KiB
TypeScript
import { expect, test } from "@playwright/test";
|
|
|
|
import { mockLangGraphAPI, MOCK_THREAD_ID } from "./utils/mock-api";
|
|
|
|
const STOPPED_TASK_DESCRIPTION = "Research stopped reload regression";
|
|
const STOPPED_TASK_PROMPT =
|
|
"Investigate why the stopped subtask card should not remain running after reload.";
|
|
|
|
const stoppedSubtaskMessages = [
|
|
{
|
|
type: "human",
|
|
id: "msg-human-stopped-subtask",
|
|
content: [
|
|
{
|
|
type: "text",
|
|
text: "Start a subtask and then stop before the task tool returns.",
|
|
},
|
|
],
|
|
},
|
|
{
|
|
type: "ai",
|
|
id: "msg-ai-stopped-subtask",
|
|
content: "",
|
|
additional_kwargs: {},
|
|
response_metadata: {},
|
|
tool_calls: [
|
|
{
|
|
id: "call-stopped-subtask",
|
|
name: "task",
|
|
args: {
|
|
subagent_type: "general-purpose",
|
|
description: STOPPED_TASK_DESCRIPTION,
|
|
prompt: STOPPED_TASK_PROMPT,
|
|
},
|
|
type: "tool_call",
|
|
},
|
|
],
|
|
invalid_tool_calls: [],
|
|
},
|
|
];
|
|
|
|
test.describe("Subtask card", () => {
|
|
test("shows failed after a stopped task thread is reloaded", async ({
|
|
page,
|
|
}) => {
|
|
mockLangGraphAPI(page, {
|
|
threads: [
|
|
{
|
|
thread_id: MOCK_THREAD_ID,
|
|
title: "Stopped subtask",
|
|
updated_at: "2026-06-18T12:00:00Z",
|
|
messages: stoppedSubtaskMessages,
|
|
},
|
|
],
|
|
});
|
|
|
|
await page.goto(`/workspace/chats/${MOCK_THREAD_ID}`);
|
|
await page.reload();
|
|
|
|
await expect(page.getByText(STOPPED_TASK_DESCRIPTION)).toBeVisible({
|
|
timeout: 15_000,
|
|
});
|
|
await expect(page.getByText("Subtask failed")).toBeVisible();
|
|
await expect(page.getByText("Running subtask")).toHaveCount(0);
|
|
});
|
|
});
|