diff --git a/apps/web/src/components/tasks/index.tsx b/apps/web/src/components/tasks/index.tsx index 6758e061..b5397126 100644 --- a/apps/web/src/components/tasks/index.tsx +++ b/apps/web/src/components/tasks/index.tsx @@ -327,9 +327,9 @@ export function TasksSidebar({
<>
-

- {item.plan} -

+ + {item.plan} +
diff --git a/apps/web/src/components/v2/thread-card.tsx b/apps/web/src/components/v2/thread-card.tsx index d162f9c6..b3e7145d 100644 --- a/apps/web/src/components/v2/thread-card.tsx +++ b/apps/web/src/components/v2/thread-card.tsx @@ -20,6 +20,7 @@ import { cn } from "@/lib/utils"; import { TaskPlan } from "@open-swe/shared/open-swe/types"; import { getActivePlanItems } from "@open-swe/shared/open-swe/tasks"; import { InlineMarkdownText } from "../thread/markdown-text"; +import { computeThreadTitle } from "@/lib/thread"; interface ThreadCardProps { thread: ThreadMetadata; @@ -36,6 +37,7 @@ export function ThreadCard({ }: ThreadCardProps) { const router = useRouter(); + const threadTitle = computeThreadTitle(taskPlan, thread.title); const isStatusLoading = statusLoading && !status; const displayStatus = status || ("idle" as ThreadUIStatus); @@ -48,7 +50,6 @@ export function ThreadCard({ const planItems = getActivePlanItems(taskPlan); const sortedPlanItems = [...planItems].sort((a, b) => a.index - b.index); - // Find the current task (lowest index among uncompleted tasks) const currentTaskIndex = sortedPlanItems .filter((item) => !item.completed) .reduce( @@ -58,8 +59,8 @@ export function ThreadCard({ const displayCurrentIndex = currentTaskIndex === Number.POSITIVE_INFINITY - ? sortedPlanItems.length // All tasks completed - : currentTaskIndex; // +1 for 1-based display + ? sortedPlanItems.length + : currentTaskIndex; return { currentTaskIndex: displayCurrentIndex, @@ -137,7 +138,7 @@ export function ThreadCard({
- {thread.title} + {threadTitle}
diff --git a/apps/web/src/components/v2/thread-view.tsx b/apps/web/src/components/v2/thread-view.tsx index d55353b2..04119a14 100644 --- a/apps/web/src/components/v2/thread-view.tsx +++ b/apps/web/src/components/v2/thread-view.tsx @@ -188,12 +188,10 @@ export function ThreadView({ } }, [plannerStream.values, selectedTab]); - // Extract task plan from programmer stream useEffect(() => { if (programmerStream.values?.taskPlan) { setProgrammerTaskPlan(programmerStream.values.taskPlan); } else if (realTimeTaskPlan) { - // Fallback to real-time task plan if stream doesn't have it setProgrammerTaskPlan(realTimeTaskPlan); } }, [programmerStream.values, realTimeTaskPlan]); diff --git a/apps/web/src/hooks/useThreadMetadata.ts b/apps/web/src/hooks/useThreadMetadata.ts index 34a06905..b6108f61 100644 --- a/apps/web/src/hooks/useThreadMetadata.ts +++ b/apps/web/src/hooks/useThreadMetadata.ts @@ -3,7 +3,7 @@ import { ManagerGraphState } from "@open-swe/shared/open-swe/manager/types"; import { ThreadMetadata } from "@/components/v2/types"; import { useThreadStatus } from "./useThreadStatus"; import { useMemo } from "react"; -import { getThreadTitle } from "@/lib/thread"; +import { getThreadTitle, computeThreadTitle } from "@/lib/thread"; import { calculateLastActivity } from "@/lib/thread-utils"; /** @@ -18,6 +18,7 @@ export function useThreadMetadata(thread: Thread): { status, isLoading: isStatusLoading, error: statusError, + taskPlan: realTimeTaskPlan, } = useThreadStatus(thread.thread_id); const metadata: ThreadMetadata = useMemo((): ThreadMetadata => { @@ -25,14 +26,14 @@ export function useThreadMetadata(thread: Thread): { return { id: thread.thread_id, - title: getThreadTitle(thread), + title: computeThreadTitle(realTimeTaskPlan, getThreadTitle(thread)), lastActivity: calculateLastActivity(thread.updated_at), - taskCount: values?.taskPlan?.tasks.length ?? 0, + taskCount: realTimeTaskPlan?.tasks.length ?? 0, repository: values?.targetRepository ? `${values.targetRepository.owner}/${values.targetRepository.repo}` : "", branch: values?.targetRepository?.branch || "main", - taskPlan: values?.taskPlan, + taskPlan: realTimeTaskPlan, status, githubIssue: values?.githubIssueId ? { @@ -41,7 +42,7 @@ export function useThreadMetadata(thread: Thread): { } : undefined, }; - }, [thread, status]); + }, [thread, status, realTimeTaskPlan]); return { metadata, diff --git a/apps/web/src/lib/thread.ts b/apps/web/src/lib/thread.ts index bfa58126..fa69533e 100644 --- a/apps/web/src/lib/thread.ts +++ b/apps/web/src/lib/thread.ts @@ -1,8 +1,21 @@ import { Thread } from "@langchain/langgraph-sdk"; import { getMessageContentString } from "@open-swe/shared/messages"; -import { GraphState } from "@open-swe/shared/open-swe/types"; +import { GraphState, TaskPlan } from "@open-swe/shared/open-swe/types"; import { getActivePlanItems } from "@open-swe/shared/open-swe/tasks"; +export function computeThreadTitle( + taskPlan: TaskPlan | undefined, + fallbackTitle: string, +): string { + if (taskPlan?.tasks && taskPlan.tasks.length > 0) { + const firstTaskTitle = taskPlan.tasks[0]?.title; + if (firstTaskTitle && firstTaskTitle.trim()) { + return firstTaskTitle; + } + } + return fallbackTitle; +} + export function getThreadTitle = GraphState>( thread: Thread, ): string {