mirror of
https://github.com/bytedance/deer-flow.git
synced 2026-07-28 00:48:07 +00:00
* feat(frontend): add side conversations for quoted follow-ups * style(frontend): apply prettier formatting to sidecar-chat files * fix(frontend): surface sidecar cascade cleanup failures via console.warn Previously deleteSidecarThreadsForParent silently swallowed both lookup errors and per-thread deletion failures, so parent thread deletions could succeed while orphaning sidecar threads with no signal to the caller. Log a warning that includes the parent id and the failed thread ids/reasons so the leak is discoverable in telemetry, matching the existing console.warn/error pattern in this file. * fix(frontend): address all sidecar review feedback Resolve every reviewer comment on PR #3934: - input-box/hooks/sidecar-panel: clear quoted references only via an `onSent` callback that fires after the in-flight guard, so a dropped send no longer silently discards quotes (willem-bd #3550). - message-list: flip the selection toolbar below the selection when it would clip above the viewport (willem-bd #3551). - reference-metadata/thread/input-box: keep referenced ids, roles, and count arrays 1:1 parallel instead of deduping ids (willem-bd #3552). - message-list: widen selection containment to the shared assistant-turn container and hint when a selection crosses messages (willem-bd #3553). - sidecar/api: coalesce concurrent sidecar creates for one parent behind a single in-flight promise to prevent duplicates (willem-bd #3554). - sidecar-trigger/context: force-restore on trigger click so a sidecar deleted elsewhere self-heals instead of opening a dead thread (willem-bd #3555). - threads/hooks: surface sidecar cascade cleanup failures via console.warn for both lookup and per-thread deletes (Copilot). Add unit + e2e coverage for parallel metadata, atomic create, and trigger self-healing.
107 lines
3.1 KiB
TypeScript
107 lines
3.1 KiB
TypeScript
import { expect, test } from "@rstest/core";
|
|
|
|
import {
|
|
SIDECAR_METADATA_KEY,
|
|
buildSidecarThreadMetadata,
|
|
isSidecarThread,
|
|
shouldShowInPrimaryThreadLists,
|
|
} from "@/core/sidecar/thread";
|
|
|
|
test("builds sidecar thread metadata from parent thread and context", () => {
|
|
const metadata = buildSidecarThreadMetadata("parent-1", {
|
|
type: "referenced_message",
|
|
label: "Assistant message",
|
|
messageId: "msg-1",
|
|
role: "assistant",
|
|
content: "Answer",
|
|
});
|
|
|
|
expect(metadata).toEqual({
|
|
[SIDECAR_METADATA_KEY]: true,
|
|
parent_thread_id: "parent-1",
|
|
sidecar_context_type: "referenced_message",
|
|
sidecar_context_label: "Assistant message",
|
|
sidecar_context_count: 1,
|
|
referenced_message_id: "msg-1",
|
|
referenced_message_ids: ["msg-1"],
|
|
referenced_message_role: "assistant",
|
|
referenced_message_roles: ["assistant"],
|
|
});
|
|
});
|
|
|
|
test("builds searchable sidecar thread metadata from multiple contexts", () => {
|
|
const metadata = buildSidecarThreadMetadata("parent-1", [
|
|
{
|
|
type: "referenced_message",
|
|
label: "Assistant message #1",
|
|
messageId: "msg-1",
|
|
role: "assistant",
|
|
content: "First answer",
|
|
},
|
|
{
|
|
type: "referenced_message",
|
|
label: "User message #2",
|
|
messageId: "msg-2",
|
|
role: "user",
|
|
content: "Follow-up request",
|
|
},
|
|
] as never);
|
|
|
|
expect(metadata).toMatchObject({
|
|
[SIDECAR_METADATA_KEY]: true,
|
|
parent_thread_id: "parent-1",
|
|
sidecar_context_type: "referenced_message",
|
|
sidecar_context_label: "Assistant message #1",
|
|
referenced_message_id: "msg-1",
|
|
referenced_message_role: "assistant",
|
|
sidecar_context_count: 2,
|
|
referenced_message_ids: ["msg-1", "msg-2"],
|
|
referenced_message_roles: ["assistant", "user"],
|
|
});
|
|
});
|
|
|
|
test("keeps referenced ids/roles parallel when quoting one message twice", () => {
|
|
const metadata = buildSidecarThreadMetadata("parent-1", [
|
|
{
|
|
type: "referenced_message",
|
|
label: "Selected assistant text #1",
|
|
messageId: "msg-1",
|
|
role: "assistant",
|
|
content: "First fragment",
|
|
},
|
|
{
|
|
type: "referenced_message",
|
|
label: "Selected assistant text #1",
|
|
messageId: "msg-1",
|
|
role: "assistant",
|
|
content: "Second fragment",
|
|
},
|
|
] as never);
|
|
|
|
expect(metadata.sidecar_context_count).toBe(2);
|
|
expect(metadata.referenced_message_ids).toEqual(["msg-1", "msg-1"]);
|
|
expect(metadata.referenced_message_roles).toEqual(["assistant", "assistant"]);
|
|
expect(metadata.referenced_message_ids).toHaveLength(
|
|
metadata.referenced_message_roles.length,
|
|
);
|
|
expect(metadata.referenced_message_ids).toHaveLength(
|
|
metadata.sidecar_context_count,
|
|
);
|
|
});
|
|
|
|
test("identifies sidecar threads and hides them from primary thread lists", () => {
|
|
const sidecar = {
|
|
thread_id: "sidecar-1",
|
|
metadata: { [SIDECAR_METADATA_KEY]: true },
|
|
};
|
|
const primary = {
|
|
thread_id: "primary-1",
|
|
metadata: {},
|
|
};
|
|
|
|
expect(isSidecarThread(sidecar)).toBe(true);
|
|
expect(shouldShowInPrimaryThreadLists(sidecar)).toBe(false);
|
|
expect(isSidecarThread(primary)).toBe(false);
|
|
expect(shouldShowInPrimaryThreadLists(primary)).toBe(true);
|
|
});
|