Skip to content
Closed
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
1 change: 1 addition & 0 deletions agentic/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ export interface KaiUserInteraction {
uri: string;
task: string;
}[];
selectedIssues?: string[];
};
}

Expand Down
2 changes: 2 additions & 0 deletions shared/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,5 @@ export * from "./transformation";
export * from "./labelSelector";
export * from "./utils/languageMapping";
export * from "./utils/diffUtils";

export type { DiagnosticIssue, DiagnosticSummary, DiagnosticMessageValue } from "./types/types";
23 changes: 23 additions & 0 deletions shared/src/types/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ export enum ChatMessageType {
JSON = "JsonChatMessage",
Tool = "ToolChatMessage",
ModifiedFile = "ModifiedFileChatMessage",
Diagnostic = "DiagnosticChatMessage",
}

export interface QuickResponse {
Expand Down Expand Up @@ -283,3 +284,25 @@ export interface InputOutputCache<K, V, C, O> {
invalidate(input: K, opts?: O): Promise<void>;
reset(): Promise<void>;
}

export const KONVEYOR_OUTPUT_CHANNEL_NAME = "Konveyor";

export interface DiagnosticIssue {
id: string;
message: string;
uri: string;
filename: string;
selected?: boolean;
}

export interface DiagnosticSummary {
summary: string;
issuesByFile: Record<string, DiagnosticIssue[]>;
totalIssues: number;
}

export interface DiagnosticMessageValue {
message: string;
diagnosticSummary: DiagnosticSummary;
tasksData: { uri: string; task: string }[];
}
36 changes: 30 additions & 6 deletions tests/e2e/tests/agent_flow_coolstore.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,16 +95,28 @@ providers.forEach((config) => {
done = true;
break;
}
// either a Yes/No button or 'Accept all changes' button will be visible throughout the flow
const yesButton = resolutionView.locator('button').filter({ hasText: 'Yes' });
// either a Yes/No button, Fix All button, or 'Accept all changes' button will be visible throughout the flow
const enabledYesButton = resolutionView
.locator('button:enabled')
.filter({ hasText: 'Yes' });
const disabledYesButton = resolutionView
.locator('button:disabled')
.filter({ hasText: 'Yes' });
const enabledFixAllButton = resolutionView
.locator('button:enabled')
.filter({ hasText: /Fix All/ });
const acceptChangesLocator = resolutionView.locator(
'button[aria-label="Accept all changes"]'
);
const yesButtonCount = await yesButton.count();
if (yesButtonCount > lastYesButtonCount) {
lastYesButtonCount = yesButtonCount;

const enabledYesButtonCount = await enabledYesButton.count();
const disabledYesButtonCount = await disabledYesButton.count();
const enabledFixAllButtonCount = await enabledFixAllButton.count();

if (enabledYesButtonCount > 0 && enabledYesButtonCount > lastYesButtonCount) {
lastYesButtonCount = enabledYesButtonCount;
await vscodeApp.waitDefault();
await yesButton.last().click();
await enabledYesButton.last().click();
console.log('Yes button clicked');
await vscodeApp.getWindow().screenshot({
path: pathlib.join(
Expand All @@ -114,6 +126,18 @@ providers.forEach((config) => {
`${1000 - maxIterations}-yesNo.png`
),
});
} else if (disabledYesButtonCount > 0 && enabledFixAllButtonCount > 0) {
await vscodeApp.waitDefault();
await enabledFixAllButton.last().click();
console.log('Fix All button clicked (yes button was disabled)');
await vscodeApp.getWindow().screenshot({
path: pathlib.join(
SCREENSHOTS_FOLDER,
'agentic_flow_coolstore',
`${config.model.replace(/[.:]/g, '-')}`,
`${1000 - maxIterations}-fixAll.png`
),
});
} else if ((await acceptChangesLocator.count()) > 0) {
await acceptChangesLocator.last().click();
console.log('Accept all changes button clicked');
Expand Down
35 changes: 31 additions & 4 deletions vscode/src/taskManager/utils.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import { basename } from "path";
import { TasksList } from "./types";
import { DiagnosticIssue, DiagnosticSummary } from "@editor-extensions/shared";

/**
* Summarizes the tasks into a string to be displayed to the user.
* Summarizes the tasks into structured data for interactive display.
* @param tasks - The tasks to summarize.
*/
export function summarizeTasks(tasks: TasksList): string {
export function summarizeTasksStructured(tasks: TasksList): DiagnosticSummary {
const uriToTasksMap = new Map<string, string[]>();
const issuesByFile: Record<string, DiagnosticIssue[]> = {};

tasks.currentTasks.forEach((task) => {
const uri = task.getUri();
Expand All @@ -18,8 +20,21 @@ export function summarizeTasks(tasks: TasksList): string {

let summary = "### New issues:\n";
uriToTasksMap.forEach((taskList, uri) => {
summary += `- ${taskList.length} new issues in **${basename(uri)}**.\n`;
const filename = basename(uri);
summary += `- ${taskList.length} new issues in **${filename}**.\n`;

// Create structured issues for this file
const uniqueTasks = Array.from(new Set(taskList));
const fileIssues: DiagnosticIssue[] = uniqueTasks.map((task) => ({
id: `${uri}-${task}`,
message: task.length > 200 ? task.slice(0, 197) + "..." : task,
uri,
filename,
}));
Comment on lines 22 to +33
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Improve ID generation for diagnostic issues

The current ID generation using ${uri}-${task} could be problematic:

  • Task strings can be very long, creating unwieldy IDs
  • Special characters in tasks might cause issues
  • Not guaranteed unique if the same task appears multiple times

Consider using a more robust approach:

-    const fileIssues: DiagnosticIssue[] = uniqueTasks.map((task) => ({
-      id: `${uri}-${task}`,
+    const fileIssues: DiagnosticIssue[] = uniqueTasks.map((task, index) => ({
+      id: `${filename}-issue-${index}`,
       message: task.length > 200 ? task.slice(0, 197) + "..." : task,
       uri,
       filename,
     }));
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const uniqueTasks = Array.from(new Set(taskList));
const fileIssues: DiagnosticIssue[] = uniqueTasks.map((task) => ({
id: `${uri}-${task}`,
message: task.length > 200 ? task.slice(0, 197) + "..." : task,
uri,
filename,
}));
const uniqueTasks = Array.from(new Set(taskList));
const fileIssues: DiagnosticIssue[] = uniqueTasks.map((task, index) => ({
id: `${filename}-issue-${index}`,
message: task.length > 200 ? task.slice(0, 197) + "..." : task,
uri,
filename,
}));
🤖 Prompt for AI Agents
In vscode/src/taskManager/utils.ts around lines 39 to 45, the ID generation for
diagnostic issues uses `${uri}-${task}`, which can produce overly long IDs,
include problematic special characters, and may not be unique if tasks repeat.
To fix this, generate IDs by combining the URI with a stable hash or index of
the task instead of the full task string. This ensures shorter, safe, and unique
IDs for each diagnostic issue.


issuesByFile[filename] = fileIssues;

// Show first 2 issues in summary
uniqueTasks.slice(0, Math.min(2, uniqueTasks.length)).forEach((task) => {
summary += ` - ${task.length > 200 ? task.slice(0, 197) + "..." : task}\n`;
});
Expand All @@ -37,7 +52,19 @@ export function summarizeTasks(tasks: TasksList): string {
});
}

return summary;
return {
summary,
issuesByFile,
totalIssues: tasks.currentTasks.length,
};
}

/**
* Summarizes the tasks into a string to be displayed to the user.
* @param tasks - The tasks to summarize.
*/
export function summarizeTasks(tasks: TasksList): string {
return summarizeTasksStructured(tasks).summary;
}

/**
Expand Down
33 changes: 28 additions & 5 deletions vscode/src/utilities/ModifiedFiles/handleQuickResponse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export async function handleQuickResponse(
messageToken: string,
responseId: string,
state: ExtensionState,
selectedIssues?: string[], // Add selected issues parameter
): Promise<void> {
try {
try {
Expand All @@ -33,17 +34,38 @@ export async function handleQuickResponse(

// Create the workflow message with proper typing
let interactionType = responseId.startsWith("choice-") ? "choice" : "yesNo";
let responseData: { choice: number } | { yesNo: boolean } | { tasks: any; yesNo: boolean } =
responseId.startsWith("choice-")
? { choice: parseInt(responseId.split("-")[1]) }
: { yesNo: responseId === "yes" };
let responseData:
| { choice: number }
| { yesNo: boolean }
| { tasks: any; yesNo: boolean; selectedIssues?: string[] } = responseId.startsWith(
"choice-",
)
? { choice: parseInt(responseId.split("-")[1]) }
: { yesNo: responseId === "yes" };

// Check if this message is related to "tasks" interaction by looking for tasksData in the message value
if (msg.value && "tasksData" in msg.value) {
interactionType = "tasks";

// Filter tasks based on selected issues if any are selected
let filteredTasks = msg.value.tasksData as Array<{ uri: string; task: string }>;
if (selectedIssues && selectedIssues.length > 0) {
// selectedIssues contains individual issue IDs in format: "${uri}-${task}"
const selectedIssueIds = new Set(selectedIssues);

// Filter tasks to only include those that match selected issue IDs
filteredTasks = (msg.value.tasksData as Array<{ uri: string; task: string }>).filter(
(task) => {
const issueId = `${task.uri}-${task.task}`;
return selectedIssueIds.has(issueId);
},
);
}

responseData = {
tasks: msg.value.tasksData,
tasks: filteredTasks,
yesNo: responseId === "yes",
selectedIssues: selectedIssues, // Include selected issues for reference
};
}

Expand All @@ -70,6 +92,7 @@ export async function handleQuickResponse(
const resolved = state.resolvePendingInteraction(messageToken, {
responseId: responseId,
interactionType: interactionType,
selectedIssues: selectedIssues, // Include selected issues
});

if (!resolved) {
Expand Down
19 changes: 15 additions & 4 deletions vscode/src/utilities/ModifiedFiles/processMessage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,12 @@ import {
KaiWorkflowMessageType,
KaiUserInteraction,
} from "@editor-extensions/agentic";
import { flattenCurrentTasks, summarizeTasks, type TasksList } from "../../taskManager";
import { flattenCurrentTasks, type TasksList } from "../../taskManager";
import { ExtensionState } from "../../extensionState";
import { ChatMessageType, ToolMessageValue } from "@editor-extensions/shared";
import { handleModifiedFileMessage } from "./handleModifiedFile";
import { MessageQueueManager, handleUserInteractionComplete } from "./queueManager";
import { summarizeTasksStructured } from "../../taskManager/utils";

// Helper function to wait for analysis completion with timeout
const waitForAnalysisCompletion = async (state: ExtensionState): Promise<void> => {
Expand Down Expand Up @@ -47,8 +48,16 @@ const resetStuckAnalysisFlags = (state: ExtensionState): void => {
};

// Helper function to create tasks message
const createTasksMessage = (tasks: TasksList): string => {
return `It appears that my fixes caused following issues:\n\n${summarizeTasks(tasks)}\n\nDo you want me to continue fixing them?`;
const createTasksMessage = (tasks: TasksList): { message: string; diagnosticSummary: any } => {
const diagnosticSummary = summarizeTasksStructured(tasks);
return {
message: `It appears that my fixes caused following issues. Please review the issues below and select which ones you'd like me to fix. `,
diagnosticSummary: {
summary: diagnosticSummary.summary,
issuesByFile: diagnosticSummary.issuesByFile,
totalIssues: diagnosticSummary.totalIssues,
},
};
};
Comment on lines +51 to 61
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Use proper type instead of any for diagnosticSummary.

The return type uses any for diagnosticSummary. Import and use the DiagnosticSummary type from @editor-extensions/shared for type safety.

Apply this diff:

+import { DiagnosticSummary } from "@editor-extensions/shared";
+
-const createTasksMessage = (tasks: TasksList): { message: string; diagnosticSummary: any } => {
+const createTasksMessage = (tasks: TasksList): { message: string; diagnosticSummary: DiagnosticSummary } => {
   const diagnosticSummary = summarizeTasksStructured(tasks);
   return {
     message: `It appears that my fixes caused following issues. Please review the issues below and select which ones you'd like me to fix. `,
-    diagnosticSummary: {
-      summary: diagnosticSummary.summary,
-      issuesByFile: diagnosticSummary.issuesByFile,
-      totalIssues: diagnosticSummary.totalIssues,
-    },
+    diagnosticSummary,
   };
 };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const createTasksMessage = (tasks: TasksList): { message: string; diagnosticSummary: any } => {
const diagnosticSummary = summarizeTasksStructured(tasks);
return {
message: `It appears that my fixes caused following issues. Please review the issues below and select which ones you'd like me to fix. `,
diagnosticSummary: {
summary: diagnosticSummary.summary,
issuesByFile: diagnosticSummary.issuesByFile,
totalIssues: diagnosticSummary.totalIssues,
},
};
};
import { DiagnosticSummary } from "@editor-extensions/shared";
const createTasksMessage = (tasks: TasksList): { message: string; diagnosticSummary: DiagnosticSummary } => {
const diagnosticSummary = summarizeTasksStructured(tasks);
return {
message: `It appears that my fixes caused following issues. Please review the issues below and select which ones you'd like me to fix. `,
diagnosticSummary,
};
};
🤖 Prompt for AI Agents
In vscode/src/utilities/ModifiedFiles/processMessage.ts around lines 51 to 61,
the function return type currently uses `any` for `diagnosticSummary`; import
the `DiagnosticSummary` type from `@editor-extensions/shared` and replace `any`
with `DiagnosticSummary` in the function return type and the `diagnosticSummary`
variable annotation so the function signature becomes `{ message: string;
diagnosticSummary: DiagnosticSummary }`; add the appropriate type-only import at
the top of the file and ensure the object you return matches the
`DiagnosticSummary` shape.


// Helper function to handle user interaction promises uniformly
Expand Down Expand Up @@ -111,12 +120,14 @@ const handleTasksInteraction = async (
}
// Show tasks to user and wait for response
state.mutateData((draft) => {
const tasksMessage = createTasksMessage(rawTasks);
draft.chatMessages.push({
kind: ChatMessageType.String,
messageToken: msg.id,
timestamp: new Date().toISOString(),
value: {
message: createTasksMessage(rawTasks),
message: tasksMessage.message,
diagnosticSummary: tasksMessage.diagnosticSummary,
tasksData: flattenCurrentTasks(rawTasks),
},
quickResponses: [
Expand Down
4 changes: 2 additions & 2 deletions vscode/src/webviewMessageHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -173,8 +173,8 @@ const actions: {
vscode.window.showErrorMessage(`Failed to show diff with decorations: ${error}`);
}
},
QUICK_RESPONSE: async ({ responseId, messageToken }, state) => {
handleQuickResponse(messageToken, responseId, state);
QUICK_RESPONSE: async ({ responseId, messageToken, selectedIssues }, state) => {
handleQuickResponse(messageToken, responseId, state, selectedIssues);
},
FILE_RESPONSE: async ({ responseId, messageToken, path, content }, state) => {
handleFileResponse(messageToken, responseId, path, content, state);
Expand Down
2 changes: 1 addition & 1 deletion webview-ui/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import React, { useState, useEffect } from "react";
import { viewType } from "./utils/vscode";
import AnalysisPage from "./components/AnalysisPage/AnalysisPage";
import ResolutionPage from "./components/ResolutionsPage/ResolutionsPage";
import ResolutionPage from "./components/ResolutionsPage/ResolutionsPage.dev";
import { WebviewType } from "@editor-extensions/shared";
import { ExtensionStateProvider } from "./context/ExtensionStateContext";
import { ProfileManagerPage } from "./components/ProfileManager/ProfileManagerPage";
Expand Down
Loading
Loading