- {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 {