Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion src/components/GroupedHistoryView/ProjectGroup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,11 @@ export default function ProjectGroup({
const taskIdsList = project.tasks
?.map((t) => t.task_id)
.filter(Boolean) || [project.project_id];
const taskQuestions = Object.fromEntries(
(project.tasks || [])
.filter((task) => task.task_id && task.question)
.map((task) => [task.task_id, task.question])
);

setIsLoadingProject(true);
try {
Expand All @@ -144,7 +149,8 @@ export default function ProjectGroup({
question,
historyId,
taskIdsList,
project.project_name
project.project_name,
taskQuestions
);
} catch (error) {
console.error('Failed to load project:', error);
Expand Down
8 changes: 7 additions & 1 deletion src/components/HistorySidebar/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,11 @@ export default function HistorySidebar() {
const taskIdsList = project?.tasks.map(
(task: HistoryTask) => task.task_id
) || [projectId];
const taskQuestions = Object.fromEntries(
(project?.tasks || [])
.filter((task: HistoryTask) => task.task_id && task.question)
.map((task: HistoryTask) => [task.task_id, task.question])
);

// If no tasks to replay, create an empty project
if (!taskIdsList || taskIdsList.length === 0) {
Expand All @@ -165,7 +170,8 @@ export default function HistorySidebar() {
question,
historyId,
taskIdsList,
project?.project_name
project?.project_name,
taskQuestions
);
};

Expand Down
13 changes: 11 additions & 2 deletions src/components/SearchHistoryDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,10 @@ export function SearchHistoryDialog() {
projectId: string,
question: string,
historyId: string,
project?: { tasks: { task_id: string }[]; project_name?: string }
project?: {
tasks: { task_id: string; question?: string }[];
project_name?: string;
}
) => {
const existingProject = projectStore.getProjectById(projectId);
if (existingProject) {
Expand All @@ -63,14 +66,20 @@ export function SearchHistoryDialog() {
const taskIdsList = project?.tasks
?.map((t) => t.task_id)
.filter(Boolean) || [projectId];
const taskQuestions = Object.fromEntries(
(project?.tasks || [])
.filter((task) => task.task_id && task.question)
.map((task) => [task.task_id, task.question as string])
);
await loadProjectFromHistory(
projectStore,
navigate,
projectId,
question,
historyId,
taskIdsList,
project?.project_name
project?.project_name,
taskQuestions
);
}
};
Expand Down
76 changes: 33 additions & 43 deletions src/lib/replay.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,29 @@ import { ChatStore } from '@/store/chatStore';
import { ProjectStore } from '@/store/projectStore';
import { NavigateFunction } from 'react-router-dom';

const getTaskQuestion = (task: ChatStore['tasks'][string] | undefined) => {
if (!task?.messages?.length) {
return '';
}

const firstUserMessage = task.messages.find((message) => {
return (
message.role === 'user' &&
typeof message.content === 'string' &&
message.content.trim().length > 0
);
});

if (firstUserMessage?.content) {
return firstUserMessage.content.trim();
}

const firstMessage = task.messages[0];
return typeof firstMessage?.content === 'string'
? firstMessage.content.trim()
: '';
};

/**
* Load project from history with final state (no animation).
* Waits for loading to complete before navigating.
Expand All @@ -28,6 +51,8 @@ import { NavigateFunction } from 'react-router-dom';
* @param historyId - The history ID
* @param taskIdsList - Optional list of task IDs (defaults to [projectId])
* @param projectName - Optional project display name
* @param taskQuestions - Optional taskId-to-question map used to preserve
* each task's original prompt when loading multi-task projects from history
*/
export const loadProjectFromHistory = async (
projectStore: ProjectStore,
Expand All @@ -36,15 +61,17 @@ export const loadProjectFromHistory = async (
question: string,
historyId: string,
taskIdsList?: string[],
projectName?: string
projectName?: string,
taskQuestions?: Record<string, string>
) => {
const taskIds = taskIdsList || [projectId];
await projectStore.loadProjectFromHistory(
taskIds,
question,
projectId,
historyId,
projectName
projectName,
taskQuestions
);
navigate({ pathname: '/' });
};
Expand Down Expand Up @@ -94,48 +121,11 @@ export const replayActiveTask = async (
return;
}

// Extract the very first available question from all chat stores and tasks
let question = '';
let earliestTimestamp = Infinity;

// Get the project data to access all chat stores
const project = projectStore.projects[projectId];
if (project && project.chatStores) {
Object.entries(project.chatStores).forEach(
([chatStoreId, chatStoreData]: [string, any]) => {
const timestamp = project.chatStoreTimestamps[chatStoreId] || 0;
const chatState = chatStoreData.getState();

if (chatState.tasks) {
Object.values(chatState.tasks).forEach((task: any) => {
// Check messages for user content
if (task.messages && task.messages.length > 0) {
const userMessage = task.messages.find(
(msg: any) => msg.role === 'user'
);
if (
userMessage &&
userMessage.content &&
timestamp < earliestTimestamp
) {
question = userMessage.content.trim();
earliestTimestamp = timestamp;
}
}
});
}
}
);
}
let question = getTaskQuestion(chatStore.tasks[taskId]);

// Fallback to current task's first message if no question found
if (
!question &&
chatStore.tasks[taskId] &&
chatStore.tasks[taskId].messages[0]
) {
question = chatStore.tasks[taskId].messages[0].content;
console.log('[REPLAY] question fall back to ', question);
if (!question) {
console.log('[REPLAY] No user question found on active task, using fallback');
question = chatStore.tasks[taskId]?.messages?.[0]?.content || '';
}

const historyId = projectStore.getHistoryId(projectId);
Expand Down
13 changes: 11 additions & 2 deletions src/pages/Projects/Project.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,10 @@ export default function Project() {
projectId: string,
question: string,
historyId: string,
project?: { tasks: { task_id: string }[]; project_name?: string }
project?: {
tasks: { task_id: string; question?: string }[];
project_name?: string;
}
) => {
const existingProject = projectStore.getProjectById(projectId);
if (existingProject) {
Expand All @@ -204,14 +207,20 @@ export default function Project() {
const taskIdsList = project?.tasks
?.map((t) => t.task_id)
.filter(Boolean) || [projectId];
const taskQuestions = Object.fromEntries(
(project?.tasks || [])
.filter((task) => task.task_id && task.question)
.map((task) => [task.task_id, task.question as string])
);
await loadProjectFromHistory(
projectStore,
navigate,
projectId,
question,
historyId,
taskIdsList,
project?.project_name
project?.project_name,
taskQuestions
);
}
};
Expand Down
9 changes: 6 additions & 3 deletions src/store/projectStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,8 @@ interface ProjectStore {
question: string,
projectId: string,
historyId?: string,
projectName?: string
projectName?: string,
taskQuestions?: Record<string, string>
) => Promise<string>;

// Project-level queued messages management
Expand Down Expand Up @@ -635,7 +636,8 @@ const projectStore = create<ProjectStore>()((set, get) => ({
question: string,
projectId: string,
historyId?: string,
projectName?: string
projectName?: string,
taskQuestions?: Record<string, string>
) => {
const { projects, removeProject, createProject, createChatStore } = get();

Expand Down Expand Up @@ -679,7 +681,8 @@ const projectStore = create<ProjectStore>()((set, get) => ({
const chatStore = project.chatStores[chatId];
if (chatStore) {
try {
await chatStore.getState().replay(taskId, question, 0);
const taskQuestion = taskQuestions?.[taskId] || question;
await chatStore.getState().replay(taskId, taskQuestion, 0);
console.log(`[ProjectStore] Loaded task ${taskId}`);
} catch (error) {
console.error(
Expand Down
Loading