feat: implement AI checkpoint and task suggestion UI with approval flow

This commit is contained in:
Roberto Musso
2026-02-24 22:29:09 +01:00
parent 77b94e2b27
commit 5445bb0eec
11 changed files with 278 additions and 26 deletions

View File

@@ -248,7 +248,20 @@ function buildProjectTools(projectId: string): StructuredTool[] {
const match = content.match(/\[[\s\S]*\]/);
const jsonStr = match ? match[0] : '[]';
try {
JSON.parse(jsonStr);
const parsed = JSON.parse(jsonStr) as Array<{ title: string; date: string }>;
for (const s of parsed) {
const ts = new Date(s.date).getTime();
if (Number.isNaN(ts)) continue;
db.insert(checkpoints).values({
id: crypto.randomUUID(),
projectId,
title: s.title,
date: ts,
isAiSuggested: 1,
isApproved: 0,
createdAt: Date.now(),
}).run();
}
return jsonStr;
} catch {
return '[]';
@@ -263,7 +276,57 @@ function buildProjectTools(projectId: string): StructuredTool[] {
},
);
return [readProjectNotesTool, addTaskTool, getSummaryTool, suggestCheckpointsTool] as StructuredTool[];
const suggestTasksTool = tool(
async (_input: Record<string, never>) => {
const contextData = buildProjectContext(projectId);
const suggestionLlm = await getLLM();
if (!suggestionLlm) return '[]';
const result = await suggestionLlm.invoke([
new SystemMessage(
'You are a project planning assistant. Analyze the project data below and extract ' +
'actionable tasks from the notes (e.g. "set up CI pipeline", "draft proposal for client").\n\n' +
'Return ONLY a valid JSON array of objects with shape { "title": string, "description"?: string, "priority"?: "low"|"medium"|"high", "dueDate"?: string } ' +
'where dueDate is ISO 8601 format (YYYY-MM-DD) if mentioned. Return [] if no actionable tasks are found.\n\n' +
'Example: [{"title":"Set up CI pipeline","description":"Configure GitHub Actions for automated testing","priority":"high"},{"title":"Draft client proposal","dueDate":"2026-03-20"}]',
),
new HumanMessage(contextData),
]);
const content = typeof result.content === 'string' ? result.content.trim() : '[]';
const match = content.match(/\[[\s\S]*\]/);
const jsonStr = match ? match[0] : '[]';
try {
const parsed = JSON.parse(jsonStr) as Array<{ title: string; description?: string; priority?: string; dueDate?: string }>;
for (const s of parsed) {
db.insert(tasks).values({
id: crypto.randomUUID(),
projectId,
title: s.title,
description: s.description ?? null,
status: 'todo',
priority: s.priority ?? 'medium',
assignee: null,
dueDate: s.dueDate ? new Date(s.dueDate).getTime() : null,
isAiSuggested: 1,
isApproved: 0,
createdAt: Date.now(),
}).run();
}
return jsonStr;
} catch {
return '[]';
}
},
{
name: 'suggest_tasks',
description:
'Analyzes project notes for actionable tasks and returns a JSON array of ' +
'{ title: string, description?: string, priority?: string, dueDate?: string } suggested tasks. ' +
'Use when the user asks for task suggestions.',
schema: z.object({}),
},
);
return [readProjectNotesTool, addTaskTool, getSummaryTool, suggestCheckpointsTool, suggestTasksTool] as StructuredTool[];
}
// ---------------------------------------------------------------------------
@@ -379,8 +442,9 @@ You also have access to the following tools — use them proactively when approp
- add_task: Create a new task in this project. Use when the user asks to add a task.
- get_summary: Generate and save a 2-3 sentence project summary. Use when asked to summarize.
- suggest_checkpoints: Extract date-based milestone suggestions from notes. Use for timeline/checkpoint questions.
- suggest_tasks: Extract actionable task suggestions from notes. Use when the user asks for task suggestions.
When suggest_checkpoints returns a JSON array, present the checkpoints in a readable bullet-point format.` : '';
When suggest_checkpoints or suggest_tasks returns a JSON array, present the items in a readable bullet-point format.` : '';
return `You are @ProjectAgent, an AI assistant specialized in a specific project within Adiuva.