Skip to content

Commit c541819

Browse files
authored
Merge pull request #1018 from RNViththagan/copilot-agent
Add diagnostic validation and fallback integration
2 parents c9e41f1 + 7a6520b commit c541819

File tree

6 files changed

+41
-9
lines changed

6 files changed

+41
-9
lines changed

common/config/rush/pnpm-lock.yaml

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

workspaces/ballerina/ballerina-core/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@
2727
"tree-kill": "^1.2.2",
2828
"vscode-uri": "^3.0.8",
2929
"@types/mousetrap": "~1.6.11",
30-
"@types/ws": "^8.2.1"
30+
"@types/ws": "^8.2.1",
31+
"ai": "^5.0.101"
3132
},
3233
"devDependencies": {
3334
"@types/node": "^22.15.21",

workspaces/ballerina/ballerina-core/src/state-machine-types.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
import { NotificationType, RequestType } from "vscode-messenger-common";
2020
import { NodePosition, STNode } from "@wso2/syntax-tree";
21+
import { ModelMessage } from "ai";
2122
import { Command } from "./interfaces/ai-panel";
2223
import { LinePosition } from "./interfaces/common";
2324
import { Type } from "./interfaces/extended-lang-client";
@@ -434,7 +435,7 @@ export interface ChatMessage {
434435
id: string;
435436
content: string;
436437
uiResponse: string;
437-
modelMessages: any[];
438+
modelMessages: ModelMessage[];
438439
timestamp: number;
439440
checkpointId?: string;
440441
}

workspaces/ballerina/ballerina-extension/src/features/ai/service/design/design.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,14 @@ Generation stopped by user. The last in-progress task was not saved. Files have
267267
await integrateCodeToWorkspace(tempProjectPath, modifiedFilesSet);
268268
}
269269

270+
// Fallback integration: integrate any remaining modified files that weren't integrated via TaskWrite in plan mode
271+
if (isPlanModeEnabled && modifiedFiles.length > 0) {
272+
const modifiedFilesSet = new Set(modifiedFiles);
273+
await integrateCodeToWorkspace(tempProjectPath, modifiedFilesSet);
274+
console.log(`[Design] Successfully integrated files on stream completion`);
275+
modifiedFiles.length = 0;
276+
}
277+
270278
updateAndSaveChat(messageId, userMessageContent, assistantMessages, eventHandler);
271279
eventHandler({ type: "stop", command: Command.Design });
272280
AIChatStateMachine.sendEvent({

workspaces/ballerina/ballerina-extension/src/features/ai/service/libs/task_write_tool.ts

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,11 @@
1717
import { tool } from 'ai';
1818
import { z } from 'zod';
1919
import { CopilotEventHandler } from '../event';
20-
import { Task, TaskStatus, TaskTypes, Plan, AIChatMachineEventType, SourceFiles } from '@wso2/ballerina-core';
20+
import { Task, TaskStatus, TaskTypes, Plan, AIChatMachineEventType, SourceFiles, AIChatMachineContext } from '@wso2/ballerina-core';
2121
import { AIChatStateMachine } from '../../../../views/ai-panel/aiChatMachine';
2222
import { integrateCodeToWorkspace } from '../design/utils';
23+
import { checkCompilationErrors } from './diagnostics_utils';
24+
import { DIAGNOSTICS_TOOL_NAME } from './diagnostics_tool';
2325

2426
export const TASK_WRITE_TOOL_NAME = "TaskWrite";
2527

@@ -134,11 +136,11 @@ Rules:
134136

135137
const taskCategories = categorizeTasks(allTasks);
136138

137-
// TODO: Fix issue where agent updates to existing plan trigger new approval
138-
// Problem: When agent continues chat with plan updates, currentPlan state is empty
139-
// causing it to be identified as new plan and triggering approval unnecessarily.
140-
// Need to preserve plan state across chat continuations or use chat history to
141-
// detect if this is a continuation of an existing conversation with a plan.
139+
// TODO: Add tests for plan modification detection in the middle of execution
140+
// Fixed: Plan state is now preserved in the state machine across chat continuations,
141+
// preventing unnecessary approval requests when agent continues with existing plan.
142+
// Still need comprehensive tests for: mid-execution plan modifications, task reordering,
143+
// task additions/removals, and edge cases where plan changes should trigger re-approval.
142144
const isNewPlan = !existingPlan || existingPlan.tasks.length === 0;
143145
const isPlanRemodification = existingPlan && (
144146
allTasks.length !== existingPlan.tasks.length ||
@@ -304,18 +306,32 @@ async function handlePlanApproval(
304306
async function handleTaskCompletion(
305307
allTasks: Task[],
306308
newlyCompletedTasks: Task[],
307-
currentContext: any,
309+
currentContext: AIChatMachineContext,
308310
eventHandler: CopilotEventHandler,
309311
tempProjectPath?: string,
310312
modifiedFiles?: string[]
311313
): Promise<{ approved: boolean; comment?: string; approvedTaskDescription: string }> {
312314
const lastCompletedTask = newlyCompletedTasks[newlyCompletedTasks.length - 1];
313315
console.log(`[TaskWrite Tool] Detected ${newlyCompletedTasks.length} newly completed task(s)`);
314316

317+
const diagnosticResult = await checkCompilationErrors(tempProjectPath!);
318+
319+
if (diagnosticResult.diagnostics.length > 0) {
320+
const errorCount = diagnosticResult.diagnostics.length;
321+
console.error(`[TaskWrite Tool] Found ${errorCount} compilation error(s), blocking task completion`);
322+
323+
return {
324+
approved: false,
325+
comment: `Cannot complete task: ${errorCount} compilation error(s) detected. Use the ${DIAGNOSTICS_TOOL_NAME} tool to check the errors and fix them before marking the task as completed.`,
326+
approvedTaskDescription: lastCompletedTask.description
327+
};
328+
}
329+
315330
if (tempProjectPath && modifiedFiles) {
316331
const modifiedFilesSet = new Set(modifiedFiles);
317332
console.log(`[TaskWrite Tool] Integrating ${modifiedFilesSet.size} modified file(s)`);
318333
await integrateCodeToWorkspace(tempProjectPath, modifiedFilesSet);
334+
modifiedFiles.length = 0;
319335
}
320336

321337
AIChatStateMachine.sendEvent({

workspaces/ballerina/ballerina-visualizer/src/views/AIPanel/components/AIChat/index.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ const convertToUIMessages = (messages: UIChatHistoryMessage[]) => {
121121
};
122122

123123
// Helper function to load chat history from localStorage
124+
// TODO: Need to be removed and move chat history to statemachine fully
124125
const loadFromLocalStorage = (projectUuid: string, setMessages: React.Dispatch<React.SetStateAction<any[]>>, chatArray: ChatEntry[]) => {
125126
const localStorageFile = `chatArray-AIGenerationChat-${projectUuid}`;
126127
const storedChatArray = localStorage.getItem(localStorageFile);
@@ -315,6 +316,7 @@ const AIChat: React.FC = () => {
315316
setMessages(uiMessages);
316317

317318
chatArray = chatArray.slice(0, updatedMessages.length);
319+
// TODO: Need to be removed and move chat history to statemachine fully
318320
localStorage.setItem(`chatArray-AIGenerationChat-${projectUuid}`, JSON.stringify(chatArray));
319321

320322
setIsLoading(false);
@@ -1367,6 +1369,7 @@ const AIChat: React.FC = () => {
13671369
filepath: chatLocation,
13681370
});
13691371
integratedChatIndex = previouslyIntegratedChatIndex;
1372+
// TODO: Need to be removed and move chat history to statemachine fully
13701373
localStorage.setItem(
13711374
`chatArray-AIGenerationChat-${projectUuid}-developer-index`,
13721375
JSON.stringify({ integratedChatIndex, previouslyIntegratedChatIndex })

0 commit comments

Comments
 (0)