Skip to content

Introduce Shell tool sandboxing and connector caching#1570

Open
IsuruMaduranga wants to merge 20 commits intowso2:mainfrom
IsuruMaduranga:mi-agent-mode
Open

Introduce Shell tool sandboxing and connector caching#1570
IsuruMaduranga wants to merge 20 commits intowso2:mainfrom
IsuruMaduranga:mi-agent-mode

Conversation

@IsuruMaduranga
Copy link
Contributor

@IsuruMaduranga IsuruMaduranga commented Feb 25, 2026

Purpose

Fixes:

Add a few authentication and stability improvements

Summary by CodeRabbit

Release Notes

  • New Features

    • Added shell command approval workflow with per-session rule persistence
    • Introduced structured file patching with context-aware hunk application
    • Added PowerShell command analysis and sandboxing for Windows
    • Implemented stream timeout management with watchdog controls
    • Added continuation workflow when output token or tool call limits are reached
  • Improvements

    • Enhanced connector store with intelligent caching and fallback handling
    • Improved authentication flow with platform extension integration
    • Refined approval UI with remember-for-session preferences
    • Updated Synapse expression and connector documentation guidance
    • Enhanced usage API integration with better error handling

…r names and improve operation name management
…n creation for projects, and enhance AIChatFooter tooltip alignment
…cessing logic for connector summaries and details
Copilot AI review requested due to automatic review settings February 25, 2026 17:57
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 25, 2026

📝 Walkthrough

Walkthrough

Introduces shell command approval flow with session-based rule persistence, stream watchdog timeout management, structured file patching, comprehensive shell sandbox analysis with PowerShell support, connector store caching layer, and integration throughout agent execution, authentication, and frontend components.

Changes

Cohort / File(s) Summary
RPC Types & Agent Mode Fundamentals
workspaces/mi/mi-core/src/rpc-types/agent-mode/types.ts, workspaces/mi/mi-extension/src/ai-features/agent-mode/agent.ts, workspaces/mi/mi-extension/src/ai-features/agent-mode/tools/types.ts
Added PlanApprovalKind union members (shell_command, continue_after_limit), extended AgentEvent and PlanApprovalResponse with suggestedPrefixRule and rememberForSession fields; updated agent.ts with continuation semantics and ShellApprovalRuleStore integration.
Stream Guard & Timeout Utilities
workspaces/mi/mi-extension/src/ai-features/agent-mode/stream_guard.ts
New module providing StreamWatchdog interface, timeout/error helpers, and createStreamWatchdog factory for managing idle/total timeouts with activity tracking and abort signaling.
Shell Sandbox Analysis (Generic & PowerShell)
workspaces/mi/mi-extension/src/ai-features/agent-mode/tools/shell_sandbox.ts, workspaces/mi/mi-extension/src/ai-features/agent-mode/tools/shell_sandbox_powershell.ts, workspaces/mi/mi-extension/src/ai-features/agent-mode/tools/shell_sandbox_powershell_ast.ts
Comprehensive shell command analysis system with platform-aware routing, destructive/sensitive path detection, approval rules matching, and PowerShell AST parsing support.
Shell Command Approval & Bash Tools
workspaces/mi/mi-extension/src/ai-features/agent-mode/tools/bash_tools.ts
Extended createBashExecute with approval flow, session rule bypass logic, and persistent rule storage integration; added formatApprovalReasons and requestShellApproval helpers.
File Editing & Patching
workspaces/mi/mi-extension/src/ai-features/agent-mode/tools/file_edit_patch.ts, workspaces/mi/mi-extension/src/ai-features/agent-mode/tools/file_tools.ts
New file_edit_patch module with PatchApplyResult types and applyStructuredFilePatch function; refactored file_tools.ts to use structured hunks instead of simple string replacement.
Connector Store Cache & Resolution
workspaces/mi/mi-extension/src/ai-features/agent-mode/tools/connector_store_cache.ts, workspaces/mi/mi-extension/scripts/update-connector-context-db.js
New connector store caching layer with ConnectorStoreCatalog interface, definition lookup logic, fallback-to-local-db behavior; added Node.js script for updating connector context definitions.
Connector & Tools Configuration
workspaces/mi/mi-extension/src/ai-features/agent-mode/tools/connector_tools.ts, workspaces/mi/mi-extension/src/ai-features/agent-mode/tools/project_tools.ts, workspaces/mi/mi-extension/.env.example
Updated connector tool signature from array-based to single-name lookups with dual-lookup strategy; refactored project_tools to use connector store cache; added MI_CONNECTOR_STORE_BACKEND endpoints.
Agent Execution & Plan Mode
workspaces/mi/mi-extension/src/ai-features/agent-mode/agents/main/agent.ts, workspaces/mi/mi-extension/src/ai-features/agent-mode/agents/main/tools.ts, workspaces/mi/mi-extension/src/ai-features/agent-mode/agents/main/prompt.ts, workspaces/mi/mi-extension/src/ai-features/agent-mode/agents/main/system.ts
Integrated stream watchdog and continuation semantics into agent execution; wired shellApprovalRuleStore through tool creation pipeline; enhanced prompt/system with connector store reminders and updated guidance.
Plan Mode & Tool Action Mapping
workspaces/mi/mi-extension/src/ai-features/agent-mode/tools/plan_mode_tools.ts, workspaces/mi/mi-extension/src/ai-features/agent-mode/tool-action-mapper.ts, workspaces/mi/mi-extension/src/ai-features/agent-mode/tools/subagent_tool.ts, workspaces/mi/mi-extension/src/ai-features/agent-mode/tools/data_mapper_tools.ts, workspaces/mi/mi-extension/src/ai-features/agent-mode/tools/web_tools.ts
Extended PendingPlanApproval and plan approval flow with rememberForSession and suggestedPrefixRule; updated CONNECTOR_TOOL_NAME handling for dynamic naming; added runtime version validation to data mapper; refined tool descriptions.
Chat History & Context Guides
workspaces/mi/mi-extension/src/ai-features/agent-mode/chat-history-manager.ts, workspaces/mi/mi-extension/src/ai-features/agent-mode/context/connectors_guide.ts, workspaces/mi/mi-extension/src/ai-features/agent-mode/context/synapse_expression_examples.ts, workspaces/mi/mi-extension/src/ai-features/agent-mode/context/synapse_expression_guide.ts, workspaces/mi/mi-extension/src/ai-features/agent-mode/context/synapse_guide.ts, workspaces/mi/mi-extension/src/ai-features/agent-mode/context/synapse_guide_old.ts
Updated storage version and interruption context; substantially rewrote CONNECTOR_DOCUMENTATION, SYNAPSE_EXPRESSION_EXAMPLES, and SYNAPSE_GUIDE with hierarchical structure, deprecation guidance, and updated resource paths.
RPC Managers (Agent & AI Features)
workspaces/mi/mi-extension/src/rpc-managers/agent-mode/rpc-manager.ts, workspaces/mi/mi-extension/src/rpc-managers/ai-features/rpc-manager.ts
Added shell approval rules lifecycle management, continuation-after-limit workflow, and session-scoped rule persistence; enhanced usage parsing pipeline with SSE/NDJSON support and quota management.
Authentication & Connection
workspaces/mi/mi-extension/src/ai-features/auth.ts, workspaces/mi/mi-extension/src/ai-features/connection.ts, workspaces/mi/mi-extension/src/ai-features/aiMachine.ts
Added STS token unavailability detection, getCopilotUsageApiUrl export, silent platform bootstrap flow; updated token refresh and re-login error handling paths.
Diagram RPC & Platform Integration
workspaces/mi/mi-extension/src/rpc-managers/mi-diagram/rpc-manager.ts
Replaced direct VSCode extension activation with getPlatformExtensionAPI() for platform extension initialization.
Frontend Components
workspaces/mi/mi-visualizer/src/views/AIPanel/component/AIChatFooter.tsx, workspaces/mi/mi-visualizer/src/views/AIPanel/component/AIChatHeader.tsx, workspaces/mi/mi-visualizer/src/views/AIPanel/component/MICopilotContext.tsx, workspaces/mi/mi-visualizer/src/views/AIPanel/utils.ts
Extended PendingPlanApproval with shell_command and continue_after_limit approval kinds; added rememberShellApprovalForSession state; introduced formatResetTime helper; updated usage calculation to handle -1 (unlimited) and seconds-based reset times.
Test Suites
workspaces/mi/mi-extension/src/test/suite/file-edit.patch.test.ts, workspaces/mi/mi-extension/src/test/suite/shell-sandbox.test.ts, workspaces/mi/mi-extension/src/test/suite/shell-sandbox.powershell.test.ts
Added comprehensive test coverage for structured file patching (ambiguity, overlap, whitespace handling), generic shell sandbox analysis (safe/unsafe commands, path sensitivity, rules matching), and PowerShell-specific analysis (escape hatch blocking, provider restrictions).

Sequence Diagram(s)

sequenceDiagram
    participant Agent as Agent
    participant BashTool as Bash Tool
    participant Sandbox as Shell Sandbox<br/>(Analysis)
    participant Store as Approval Rule<br/>Store
    participant Handler as Event Handler<br/>(Approval)
    participant User as User

    Agent->>BashTool: Execute shell command
    BashTool->>Sandbox: analyzeShellCommand()
    Sandbox->>Sandbox: Parse & classify<br/>(safe/destructive/sensitive)
    Sandbox-->>BashTool: ShellCommandAnalysis<br/>(requiresApproval,<br/>blocked, reasons)
    
    alt Command Blocked
        BashTool-->>Agent: buildShellSandboxBlockedResult()
    else Approval Needed
        BashTool->>Store: Check isAnalysisCoveredByRules()
        Store-->>BashTool: Coverage result
        alt Rules Cover Command
            BashTool->>BashTool: Bypass approval
            BashTool->>Agent: Execute command
        else Rules Don't Cover
            BashTool->>Handler: requestShellApproval()
            Handler->>User: Emit approval request<br/>event with reasons
            User-->>Handler: Approve/Deny +<br/>rememberForSession?
            Handler-->>BashTool: Approval response
            alt User Approved & Remember
                BashTool->>Store: addRule()<br/>(suggested prefix)
                Store-->>BashTool: Rule persisted
            end
            alt User Approved
                BashTool->>Agent: Execute command
            else User Denied
                BashTool-->>Agent: Approval denied
            end
        end
    else Safe Command
        BashTool->>Agent: Execute command
    end
Loading
sequenceDiagram
    participant Agent as Agent Runtime
    participant Watchdog as Stream Watchdog
    participant LLM as LLM Stream
    participant Tools as Tool Execution
    participant Handler as Error/Timeout<br/>Handler

    Agent->>Watchdog: createStreamWatchdog()<br/>(idleTimeout,<br/>totalTimeout)
    
    loop Stream Processing
        Tools->>Watchdog: markActivity()
        Watchdog->>Watchdog: Reset idle timer
        
        Tools->>LLM: Stream tokens
        LLM-->>Tools: Content/Delta
        Tools-->>Agent: Process token
    end
    
    alt Idle Timeout Exceeded
        Watchdog->>Watchdog: Trigger idle<br/>timeout
        Watchdog->>Watchdog: createStreamTimeoutError()
        Watchdog->>Handler: onTimeout(error)
        Handler->>Agent: Emit error event
        Handler->>Watchdog: abort()
    else Total Timeout Exceeded
        Watchdog->>Watchdog: Trigger total<br/>timeout
        Watchdog->>Handler: onTimeout(error)
        Handler->>Agent: Emit error event
        Handler->>Watchdog: abort()
    else Stream Completes
        LLM-->>Tools: Stream end
        Tools->>Watchdog: cleanup()
        Watchdog->>Agent: Return result with<br/>continuationSuggested?
    end
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Poem

🐰 Whiskers twitching with glee...
Shells now seek approval with finesse,
Stream watchdogs guard the forest's rest,
Patches stitch files with elegant art,
Rules remember when sessions start,
A connector cache blooms fresh and bright—
The agent leaps into the light! ✨

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The PR description is significantly incomplete, missing most required sections from the repository template including goals, approach, UI component development, documentation, testing, security checks, and other essential details. Complete the PR description by filling in all required sections: Goals, Approach (with details on implementation), Documentation links, Test coverage (unit and integration), Security checks, and any other applicable sections from the template.
Docstring Coverage ⚠️ Warning Docstring coverage is 7.22% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The PR title 'Introduce Shell tool sandboxing and connector caching' clearly and concisely summarizes the main changes in the changeset.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR introduces two major features and several stability improvements to the MI VS Code extension's AI agent capabilities:

Purpose: Add shell command sandboxing with user approval workflows and implement granular connector store caching to improve resilience and performance.

Changes:

  1. Shell tool sandboxing with policy-based approval system for potentially destructive commands
  2. Connector store caching infrastructure with per-definition granular caching and fallback mechanisms
  3. Agent execution improvements including stream watchdog, timeout handling, and continuation after limits
  4. Authentication improvements for STS token handling
  5. UI improvements for time formatting (seconds instead of days) and shell approval dialogs

Reviewed changes

Copilot reviewed 42 out of 44 changed files in this pull request and generated no comments.

Show a summary per file
File Description
shell_sandbox*.ts New shell command analysis and sandboxing implementation (3 files)
file_edit_patch.ts New structured hunk-based file editing with context/line hints
connector_store_cache.ts Rewritten caching with per-definition granular cache files
bash_tools.ts Integrated shell sandbox approval flow
agent.ts Added stream watchdog, timeout handling, and continuation logic
auth.ts Improved STS token error handling
utils.ts Changed time display from days to seconds
AIChatFooter.tsx Added shell approval UI with "remember for session" checkbox
AIChatHeader.tsx Added formatResetTime helper for seconds-based display
stream_guard.ts New stream lifecycle management with idle/total timeouts
connector_tools.ts Changed to single-item lookup API pattern
project_tools.ts Updated to use new connector lookup APIs
system.ts Updated system prompt with new tool usage guidelines
synapse_guide*.ts Documentation improvements and clarifications
Test files (3) Comprehensive tests for shell sandbox and file patch
update-connector-context-db.js New script for updating connector fallback data

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 14

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
workspaces/mi/mi-extension/src/ai-features/agent-mode/tools/data_mapper_tools.ts (1)

428-435: ⚠️ Potential issue | 🟡 Minor

Checkpoint captured before mapFunction validity check.

captureBeforeChange (line 429) runs unconditionally before the mapFunction guard on line 430. If the file fails the check, the undo history contains a spurious entry for a file that was never modified.

♻️ Proposed fix — move checkpoint after validation
-            const content = fs.readFileSync(tsFilePath, 'utf8');
-            await undoCheckpointManager?.captureBeforeChange(path.relative(projectPath, tsFilePath));
-            if (!content.includes('mapFunction')) {
+            const content = fs.readFileSync(tsFilePath, 'utf8');
+            if (!content.includes('mapFunction')) {
                 return {
                     success: false,
                     message: `File at ${tsFilePath} does not appear to be a valid data mapper file (missing mapFunction).`,
                     error: 'Error: Invalid data mapper file',
                 };
             }
+            await undoCheckpointManager?.captureBeforeChange(path.relative(projectPath, tsFilePath));
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@workspaces/mi/mi-extension/src/ai-features/agent-mode/tools/data_mapper_tools.ts`
around lines 428 - 435, The code currently calls
undoCheckpointManager?.captureBeforeChange(path.relative(projectPath,
tsFilePath)) before validating the file contains mapFunction, which creates
unnecessary undo entries for invalid files; move the captureBeforeChange call so
it runs only after you verify content.includes('mapFunction') (i.e., after the
guard that returns the invalid-file error) to ensure checkpoints are only
created for files that will be modified; update references to
undoCheckpointManager.captureBeforeChange, mapFunction check, tsFilePath and
projectPath accordingly.
workspaces/mi/mi-extension/src/ai-features/agent-mode/tools/project_tools.ts (1)

312-318: ⚠️ Potential issue | 🟡 Minor

version may be undefined, potentially producing an invalid pom.xml entry.

item.version?.tagName can be undefined if the resolved item lacks version info (e.g., from a fallback definition). This would pass undefined as the version in the dependency, which could result in a malformed pom.xml update or a silent failure downstream.

Consider validating version alongside the Maven coordinates check at Line 283, or defaulting to a known version.

Proposed validation addition
-        if (typeof item?.mavenGroupId !== 'string' || typeof item?.mavenArtifactId !== 'string') {
+        if (typeof item?.mavenGroupId !== 'string' || typeof item?.mavenArtifactId !== 'string' || !item?.version?.tagName) {
             return {
                 name: itemName,
                 type: itemType,
                 success: false,
-                error: `${itemType === 'connector' ? 'Connector' : 'Inbound endpoint'} definition is missing Maven coordinates`
+                error: `${itemType === 'connector' ? 'Connector' : 'Inbound endpoint'} definition is missing Maven coordinates or version`
             };
         }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@workspaces/mi/mi-extension/src/ai-features/agent-mode/tools/project_tools.ts`
around lines 312 - 318, The dependency object is using item.version?.tagName
which can be undefined and yield an invalid pom entry; update the code before
constructing dependencies (the block creating the dependencies:
DependencyDetails[] and the use of item.mavenGroupId/item.mavenArtifactId) to
validate that item.version?.tagName exists (or supply a safe default) and treat
missing version as an error or skip the dependency: e.g., check the same place
where Maven coordinates are validated (the earlier Maven check) and either
throw/log and return/continue when version is missing, or set version to a known
default string; ensure the dependency entry uses the validated non-undefined
version value instead of item.version?.tagName.
🧹 Nitpick comments (13)
workspaces/mi/mi-extension/scripts/update-connector-context-db.js (1)

10-11: Minor: using an escaped ${...} inside a template literal as a manual replacement token is confusing.

`${API_BASE}/connectors/summaries?type=\${type}&...` produces a string containing the literal text ${type}, which is then replaced via .replace('${type}', ...). A plain function or a regular string with a placeholder like TYPE_PLACEHOLDER is clearer.

♻️ Cleaner alternative
-const SUMMARY_URL_TEMPLATE = `${API_BASE}/connectors/summaries?type=\${type}&limit=100&offset=0&product=MI`;
+function buildSummaryUrl(type) {
+    return `${API_BASE}/connectors/summaries?type=${encodeURIComponent(type)}&limit=100&offset=0&product=MI`;
+}

Then in fetchSummaries:

-    const summaryUrl = SUMMARY_URL_TEMPLATE.replace('${type}', encodeURIComponent(type));
+    const summaryUrl = buildSummaryUrl(type);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@workspaces/mi/mi-extension/scripts/update-connector-context-db.js` around
lines 10 - 11, The SUMMARY_URL_TEMPLATE currently embeds an escaped `${type}`
token which yields a literal `${type}` and is later replaced, which is
confusing; change SUMMARY_URL_TEMPLATE to use a clear placeholder (e.g.
'TYPE_PLACEHOLDER') or make it a function like getSummaryUrl(type) that returns
`${API_BASE}/connectors/summaries?type=${type}&limit=100&offset=0&product=MI`;
then update fetchSummaries to call the new function or replace the new
placeholder instead of relying on the escaped `${type}` token (references:
SUMMARY_URL_TEMPLATE and fetchSummaries).
workspaces/mi/mi-extension/src/ai-features/agent-mode/agents/main/tools.ts (1)

118-118: Consolidate the duplicate import from ../../tools/types.

Line 118 opens a second import block from the same module path already imported in Lines 94–117. Merge BashExecuteFn, ToolResult, and ShellApprovalRuleStore into the existing import.

♻️ Proposed consolidation
 import {
     FILE_WRITE_TOOL_NAME,
     ...
     WEB_SEARCH_TOOL_NAME,
     WEB_FETCH_TOOL_NAME,
+    BashExecuteFn,
+    ToolResult,
+    ShellApprovalRuleStore,
 } from '../../tools/types';
-import { BashExecuteFn, ToolResult, ShellApprovalRuleStore } from '../../tools/types';
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@workspaces/mi/mi-extension/src/ai-features/agent-mode/agents/main/tools.ts`
at line 118, Remove the duplicate import statement that brings in BashExecuteFn,
ToolResult, and ShellApprovalRuleStore and instead add these three symbols to
the existing import from '../../tools/types' (the earlier import block that
already imports other types from that module); ensure there are no duplicate
specifiers, keep the import list sorted/consistent with the project's style, and
run the linter/TS compile to confirm the redundant import is gone.
workspaces/mi/mi-extension/.env.example (1)

14-15: Optional: sort new keys to match existing alphabetical ordering.

dotenv-linter flags both new keys as out of order. MI_CONNECTOR_STORE_BACKEND_SUMMARIES should precede MI_COPILOT_ANTHROPIC_PROXY_URL, and MI_CONNECTOR_STORE_BACKEND_DETAILS_FILTER should precede MI_CONNECTOR_STORE_BACKEND_GETBYVERSION.

♻️ Proposed ordering
 MI_CONNECTOR_STORE_BACKEND=https://...
+MI_CONNECTOR_STORE_BACKEND_DETAILS_FILTER=https://apis.wso2.com/qgpf/connector-store-backend/endpoint-9090-803/v1.0/connectors/details/filter
 MI_CONNECTOR_STORE_BACKEND_GETBYVERSION=https://...
-MI_CONNECTOR_STORE_BACKEND_SUMMARIES=https://...
-MI_CONNECTOR_STORE_BACKEND_DETAILS_FILTER=https://...
+MI_CONNECTOR_STORE_BACKEND_SUMMARIES=https://apis.wso2.com/qgpf/connector-store-backend/endpoint-9090-803/v1.0/connectors/summaries?type=${type}&limit=100&offset=0&product=MI
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@workspaces/mi/mi-extension/.env.example` around lines 14 - 15, Reorder the
two new environment keys so they follow the file's alphabetical ordering: move
MI_CONNECTOR_STORE_BACKEND_SUMMARIES to appear before
MI_COPILOT_ANTHROPIC_PROXY_URL, and move
MI_CONNECTOR_STORE_BACKEND_DETAILS_FILTER to appear before
MI_CONNECTOR_STORE_BACKEND_GETBYVERSION; update .env.example accordingly and
re-run dotenv-linter to confirm no ordering warnings remain.
workspaces/mi/mi-extension/src/test/suite/file-edit.patch.test.ts (1)

22-163: Solid test coverage for the new patching module.

Good mix of happy-path and error-path tests. Two additional edge cases worth covering:

  1. Deletion hunk: { old_text: 'line_to_delete', new_text: '' } — verifies splitReplacementLines returns [] and lines are removed.
  2. Equidistant line_hint: Two matches equally close to the hint — should this resolve or return HUNK_AMBIGUOUS?

These can be added as follow-ups.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@workspaces/mi/mi-extension/src/test/suite/file-edit.patch.test.ts` around
lines 22 - 163, Add two new tests to the File Edit Patch Tests suite to cover
(1) a deletion hunk and (2) the equidistant line_hint edge case: create a test
calling applyStructuredFilePatch with a hunk where old_text is 'line_to_delete'
and new_text is '' and assert success=true, newContent no longer contains that
line and splitReplacementLines behavior (empty array) is respected; and create a
test where the content has two identical matches equidistant from a provided
line_hint, call applyStructuredFilePatch with that line_hint and assert the
expected behavior (either success with deterministic selection or success=false
with code 'HUNK_AMBIGUOUS' per product decision). Reference
applyStructuredFilePatch and splitReplacementLines when adding assertions so
reviewers can validate the intended behavior and expected result code.
workspaces/mi/mi-extension/src/ai-features/aiMachine.ts (1)

196-207: Both COMPLETE_AUTH and SIGN_IN_SUCCESS in Unauthenticated are identical — consider whether one event suffices.

Both transitions do the same thing: move to Authenticated and clear errorMessage. The semantic distinction (explicit auth vs. silent bootstrap) is the only difference. This is fine for traceability, but if maintaining two identical transitions becomes a burden, they could be consolidated.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@workspaces/mi/mi-extension/src/ai-features/aiMachine.ts` around lines 196 -
207, In the Unauthenticated state both AI_EVENT_TYPE.COMPLETE_AUTH and
AI_EVENT_TYPE.SIGN_IN_SUCCESS transition to 'Authenticated' and clear
errorMessage; consolidate them by handling a single event (e.g., map
SIGN_IN_SUCCESS to COMPLETE_AUTH or collapse both into one shared transition
handler) or extract the shared transition into a named transition/action used by
both to avoid duplication; update the transition definitions around the
Unauthenticated state's handlers (AI_EVENT_TYPE.COMPLETE_AUTH,
AI_EVENT_TYPE.SIGN_IN_SUCCESS) and ensure traceability by optionally adding a
short comment indicating why events were consolidated or kept distinct.
workspaces/mi/mi-extension/src/ai-features/agent-mode/tools/file_edit_patch.ts (1)

145-163: Validation: old_text === new_text check uses raw string comparison, but matching uses normalized lines.

If old_text and new_text differ only in trailing whitespace (e.g., "foo " vs "foo"), this validation passes (they're not string-equal), but after normalization the old_text match will succeed and the replacement will effectively only change trailing whitespace. This is an acceptable edge case — it's a valid edit — but worth noting that the "identical" check is less strict than the matching.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@workspaces/mi/mi-extension/src/ai-features/agent-mode/tools/file_edit_patch.ts`
around lines 145 - 163, The validateHunk function currently compares
FileEditHunk.old_text and .new_text with a raw string equality which can miss
cases where only normalization (trimming/trailing whitespace normalization or
line-ending normalization) makes them identical; update validateHunk to
normalize both old_text and new_text using the same normalization logic used
when matching/applying patches (the same routine that splits/normalizes lines
for matching) before performing the equality check so that identical content
after normalization is treated as identical; keep the existing error shape
(PatchApplyFailure) and the hunkIndex reference but use normalizedOld/new
variables when comparing in validateHunk.
workspaces/mi/mi-extension/src/ai-features/agent-mode/tools/plan_mode_tools.ts (1)

263-276: No timeout on pending plan approvals — unlike PendingQuestion which has ASK_USER_TIMEOUT_MS.

requestPlanApproval creates a promise that stays pending indefinitely if the user never responds (e.g., closes the panel). PendingQuestion has a 5-minute timeout + cleanup via cleanupPendingQuestionsForSession, but PendingPlanApproval has neither. If the session is abandoned, the promise leaks along with the entry in pendingApprovals.

Consider adding a timeout or session-cleanup hook for pending approvals, similar to cleanupPendingQuestionsForSession.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@workspaces/mi/mi-extension/src/ai-features/agent-mode/tools/plan_mode_tools.ts`
around lines 263 - 276, The pending plan-approval promise created in
requestPlanApproval (and stored in pendingApprovals / PendingPlanApproval) has
no timeout or session cleanup and can leak; add a timeout similar to
PendingQuestion using ASK_USER_TIMEOUT_MS: when creating the pending approval,
start a timer that rejects the promise with a timeout Error and deletes the
entry from pendingApprovals, and store/clear that timer so resolve/reject
callbacks clear it; also ensure cleanupPendingQuestionsForSession (or an
analogous cleanup function) removes any pendingApprovals for an abandoned
session to avoid leaks.
workspaces/mi/mi-extension/src/ai-features/agent-mode/tools/data_mapper_tools.ts (1)

409-417: Redundant resolveProjectBoundPath call.

By the time line 409 is reached, tsFilePath already holds the result of resolveProjectBoundPath from one of the earlier branches (lines 392–406). The second call is a no-op in the success path and only adds overhead. Consider removing it and keeping only the undefined guard from lines 393–398.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@workspaces/mi/mi-extension/src/ai-features/agent-mode/tools/data_mapper_tools.ts`
around lines 409 - 417, The second call to resolveProjectBoundPath is redundant
because tsFilePath already holds its result in the earlier branch; remove the
duplicate call and simply keep the undefined guard/early-return that checks
tsFilePath (and returns the error object referencing dm_config_path), i.e.
delete the later resolveProjectBoundPath invocation and ensure the code uses the
existing tsFilePath variable and its null/undefined check to preserve the same
early-return behavior; references: resolveProjectBoundPath, tsFilePath,
dm_config_path.
workspaces/mi/mi-visualizer/src/views/AIPanel/component/MICopilotContext.tsx (1)

40-45: Import and use PlanApprovalKind from @wso2/mi-core instead of duplicating the union locally.

The approvalKind field redefines a union type that is already available as PlanApprovalKind in @wso2/mi-core. Maintaining a parallel inline union means any future changes to the core type must be manually mirrored here, risking silent mismatches between backend and UI.

♻️ Proposed refactor
-import { FileObject, ImageObject, TodoItem, Question } from "@wso2/mi-core";
+import { FileObject, ImageObject, TodoItem, Question, PlanApprovalKind } from "@wso2/mi-core";
 export interface PendingPlanApproval {
     approvalId: string;
-    approvalKind?: 'enter_plan_mode' | 'exit_plan_mode' | 'exit_plan_mode_without_plan' | 'web_search' | 'web_fetch' | 'shell_command' | 'continue_after_limit';
+    approvalKind?: PlanApprovalKind;
     approvalTitle?: string;
     approveLabel?: string;
     rejectLabel?: string;
     allowFeedback?: boolean;
     suggestedPrefixRule?: string[];
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@workspaces/mi/mi-visualizer/src/views/AIPanel/component/MICopilotContext.tsx`
around lines 40 - 45, Replace the inline union type used for the approvalKind
prop in MICopilotContext with the canonical PlanApprovalKind from `@wso2/mi-core`:
import PlanApprovalKind and change the approvalKind?: 'enter_plan_mode' | ...
declaration to approvalKind?: PlanApprovalKind, updating any references/usages
in MICopilotContext to the imported type so the UI stays in sync with the core
type and avoids duplication.
workspaces/mi/mi-extension/src/rpc-managers/mi-diagram/rpc-manager.ts (1)

4926-4928: Return a consistent DevantMetadata shape in the API-unavailable branch.

This branch omits hasLocalChanges while other branches include it. Returning an explicit false keeps downstream handling predictable.

Suggested patch
-            if (!platformExtAPI) {
-                return { hasComponent: hasContextYaml, isLoggedIn: false };
-            }
+            if (!platformExtAPI) {
+                return { hasComponent: hasContextYaml, isLoggedIn: false, hasLocalChanges: false };
+            }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@workspaces/mi/mi-extension/src/rpc-managers/mi-diagram/rpc-manager.ts` around
lines 4926 - 4928, The API-unavailable branch in rpc-manager.ts returns {
hasComponent: hasContextYaml, isLoggedIn: false } but omits hasLocalChanges;
update that return to include hasLocalChanges: false so the DevantMetadata shape
is consistent (modify the branch that checks platformExtAPI and returns when
falsy to return { hasComponent: hasContextYaml, isLoggedIn: false,
hasLocalChanges: false }).
workspaces/mi/mi-extension/src/ai-features/agent-mode/tools/shell_sandbox_powershell_ast.ts (1)

71-73: Cold-start latency consideration for PowerShell parser.

powershell.exe cold start on Windows can take 500ms–2s. On the first shell command of a session, this could make the 1500ms timeout tight. If timeouts are frequent in practice, consider bumping PARSER_TIMEOUT_MS slightly or caching the "known working binary" after first successful parse to skip the powershell.exepwsh fallback chain on subsequent calls.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@workspaces/mi/mi-extension/src/ai-features/agent-mode/tools/shell_sandbox_powershell_ast.ts`
around lines 71 - 73, The current PARSER_TIMEOUT_MS (1500) is likely too tight
given powershell.exe cold-starts; increase PARSER_TIMEOUT_MS to a safer value
(e.g., 3000) or implement caching of the confirmed working binary to avoid
repeatedly trying the fallback chain in PARSER_BINARIES ('powershell.exe',
'pwsh') — update the parsing logic that iterates PARSER_BINARIES to store the
first successful binary and reuse it for subsequent parses to reduce cold-start
latency.
workspaces/mi/mi-extension/src/ai-features/agent-mode/tools/project_tools.ts (1)

257-268: Consider using a proper type instead of any for resolvedItem.

resolvedItem: any | null loses type safety. If ConnectorDefinitionLookupResult.definitionsByName has a typed value, propagating that type here would catch shape mismatches at compile time.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@workspaces/mi/mi-extension/src/ai-features/agent-mode/tools/project_tools.ts`
around lines 257 - 268, The parameter resolvedItem in processItem is declared as
any|null; replace it with the concrete type from
ConnectorDefinitionLookupResult.definitionsByName (or a small union/alias that
matches that lookup's value, e.g. ConnectorDefinition or
ConnectorDefinitionLookupResult['definitionsByName'][keyof
ConnectorDefinitionLookupResult['definitionsByName']] | null) so callers get
compile-time shape checks; update the processItem signature and any call
sites/imports, and adjust any internal handling/optional checks in processItem
to match the new typed shape rather than using any.
workspaces/mi/mi-visualizer/src/views/AIPanel/component/AIChatFooter.tsx (1)

1003-1008: Only send session-rule fields on approved shell-command responses.

Line 1003 and Line 1006 currently populate rememberForSession/suggestedPrefixRule even when approved is false. Guarding these fields with approved avoids ambiguous backend semantics.

🔧 Proposed fix
-                    rememberForSession: pendingPlanApproval.approvalKind === 'shell_command'
+                    rememberForSession: pendingPlanApproval.approvalKind === 'shell_command' && approved
                         ? rememberShellApprovalForSession
                         : undefined,
-                    suggestedPrefixRule: pendingPlanApproval.approvalKind === 'shell_command' && rememberShellApprovalForSession
+                    suggestedPrefixRule: pendingPlanApproval.approvalKind === 'shell_command' && approved && rememberShellApprovalForSession
                         ? pendingPlanApproval.suggestedPrefixRule
                         : undefined,
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@workspaces/mi/mi-visualizer/src/views/AIPanel/component/AIChatFooter.tsx`
around lines 1003 - 1008, The code currently sets pendingPlanApproval session
fields regardless of approval state; update the logic in AIChatFooter (the block
that assigns rememberForSession and suggestedPrefixRule from
pendingPlanApproval) so both fields are only populated when
pendingPlanApproval.approved is true and pendingPlanApproval.approvalKind ===
'shell_command'; in particular, set rememberForSession to
rememberShellApprovalForSession only when approved && approvalKind ===
'shell_command', and set suggestedPrefixRule only when approved && approvalKind
=== 'shell_command' && rememberShellApprovalForSession, using the same property
names (pendingPlanApproval.suggestedPrefixRule) to locate the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@workspaces/mi/mi-extension/scripts/update-connector-context-db.js`:
- Around line 274-301: The current readExistingRecordsByName function is brittle
because it slices a TypeScript array literal and passes it to JSON.parse (the
JSON.parse call on the substring), which will throw on trailing commas, single
quotes or comments; replace that JSON.parse usage with a safer parser/evaluator
(e.g., JSON5.parse or evaluating the array literal in a sandboxed VM) so
TypeScript/JS syntax is accepted, and wrap the parse/eval in a try/catch to fall
back to returning an empty Map instead of crashing; update references in
readExistingRecordsByName and preserve the existing mapping logic that uses
getConnectorName to populate recordsByName.
- Around line 303-314: The writeTsArrayFile function currently overwrites
everything after the export declaration because exportRegex only matches up to
"export const <exportName> =" and the code writes match[0] + JSON without
preserving the trailing semicolon or any following content; update
writeTsArrayFile to locate the end of the array literal in the existing file
(find the closing ] for the exported array, include any trailing
whitespace/comments and the existing semicolon if present), then write back the
prefix up through "export const <exportName> =" + the new
JSON.stringify(records, null, 4) + a semicolon and the original suffix
(everything after the closing ]), ensuring the regex/exportRegex and match usage
in writeTsArrayFile are adjusted to capture or compute the suffix to preserve
subsequent declarations.

In `@workspaces/mi/mi-extension/src/ai-features/agent-mode/agents/main/agent.ts`:
- Around line 769-776: The code tries to read .messages from a possibly
undefined result of awaitWithTimeout, causing a null dereference; update each
occurrence (the block using awaitWithTimeout, finalResponseWaitTimeoutMs and
assigning finalModelMessages) to first await into a local const (e.g.,
finalResponse) and then safely access messages using optional chaining or a
fallback (e.g., finalResponse?.messages ?? []), so finalModelMessages is always
an array; apply the same safe-access pattern to the other two identical blocks
to avoid NPEs and keep the existing logError catch behavior.

In `@workspaces/mi/mi-extension/src/ai-features/agent-mode/agents/main/prompt.ts`:
- Around line 88-89: Remove or guard the hardcoded development notice string
("YOU ARE IN DEVELOPMENT PHASE...") in the prompt assembly so it is never sent
to production users; instead gate this message behind a debug/dev flag (e.g., a
config like isDevMode or process.env.DEBUG) or annotate it with a clear TODO to
delete before release. Specifically, update prompt.ts to stop injecting the
development string into the assembled prompt and ensure the variable
system_remainder is never exposed to end users (align with the non-disclosure
rule implemented in system.ts and its policy), and add a short unit/integration
check to confirm dev-only text is omitted unless debug mode is explicitly
enabled.

In `@workspaces/mi/mi-extension/src/ai-features/agent-mode/agents/main/system.ts`:
- Around line 238-246: SYSTEM_PROMPT_OLD currently substitutes SYNAPSE_GUIDE
with SYNAPSE_GUIDE_OLD but reuses CONNECTOR_DOCUMENTATION which documents
"Revamped response handling" (responseVariable, overwriteBody) not available for
older MI runtimes; fix by adding a version-appropriate connector doc (e.g.,
CONNECTOR_DOCUMENTATION_OLD) that omits the revamped response handling and use
it when building SYSTEM_PROMPT_OLD, or update the SYSTEM_PROMPT generation to
conditionally include the revamped section only when the runtime version >=
4.4.0 (check runtime version before choosing CONNECTOR_DOCUMENTATION vs
CONNECTOR_DOCUMENTATION_OLD); update references to SYSTEM_PROMPT_OLD,
SYNAPSE_GUIDE, SYNAPSE_GUIDE_OLD, and CONNECTOR_DOCUMENTATION to reflect the
chosen approach.

In
`@workspaces/mi/mi-extension/src/ai-features/agent-mode/context/synapse_expression_examples.ts`:
- Around line 92-97: In the FreeMarker JSON example inside
synapse_expression_examples.ts locate the JSON payload string that starts with
"name": "${payload.customer_name}" and add a comma after that field so the
object becomes valid JSON; ensure the resulting snippet reads "name":
"${payload.customer_name}", followed by the existing "customer_id" and other
properties (update only the punctuation in the example string).

In
`@workspaces/mi/mi-extension/src/ai-features/agent-mode/context/synapse_guide.ts`:
- Around line 445-446: Update the guide text to use "artifact.xml" consistently
instead of the mixed "artifacts.xml"/"artifact.xml" wording: replace occurrences
of the string "artifacts.xml" with "artifact.xml" (e.g., change "If an
artifacts.xml doesn't exist" to "If an artifact.xml doesn't exist") and ensure
the sentence referencing the data mapper tool (${CREATE_DATA_MAPPER_TOOL_NAME})
still reads correctly; keep the registry descriptor format example unchanged but
confirm it uses "artifact.xml" throughout so the agent won't generate the wrong
file name.
- Around line 216-225: The guidance in synapse_guide.ts currently contradicts
the expression guide by unconditionally stating to "ALWAYS" use JSON paths on
${payload} and to "NEVER" use XML/xpath(), which conflicts with examples using
$body/xpath; update this block to: state that ${payload} is JSON after SOAP call
and prefer JSON paths (e.g., payload.NumberToWordsResponse.NumberToWordsResult)
when using ${payload}, but allow xpath() or $body access only when the raw XML
body is explicitly materialized and documented by the expression engine; remove
absolute terms ("ALWAYS"/"NEVER"/"AVOID") and add a short note referencing the
expression guide's $body/xpath usage so the two sources are aligned and
unambiguous.

In
`@workspaces/mi/mi-extension/src/ai-features/agent-mode/tools/connector_store_cache.ts`:
- Around line 122-127: The code is using runtimeVersion directly in path
segments which allows path-segment injection; update buildItemDirectory and
buildCatalogFilePath to sanitize/validate runtimeVersion before using it (e.g.,
reject or canonicalize values containing path separators or traversal patterns,
or restrict to a safe regex like /^[A-Za-z0-9._-]+$/), use the sanitized value
when joining with CACHE_ROOT_DIR and CATALOG_FILE_NAME, and ensure any callers
(places referenced around pom.xml usage) pass or check the sanitized value as
well; reference functions: buildItemDirectory, buildCatalogFilePath, and
constants: CACHE_ROOT_DIR, CATALOG_FILE_NAME.

In
`@workspaces/mi/mi-extension/src/ai-features/agent-mode/tools/shell_sandbox_powershell.ts`:
- Around line 707-710: The code computes networkDetected using
POWERSHELL_NETWORK_COMMANDS and detectNetworkFromDotNet (based on commandNames
and trimmedCommand) but only bypasses the read-only allowlist check without
marking the action as requiring approval; update the approval logic so that when
networkDetected is true you set requiresApproval = true and append a clear
approvalReason (e.g., "network access detected via command/dotnet call") so
commands like curl/Invoke-WebRequest cannot slip through, and ensure this change
is applied wherever requiresApproval and approvalReason are set in the approval
flow handling (reference variables networkDetected, POWERSHELL_NETWORK_COMMANDS,
detectNetworkFromDotNet, requiresApproval, approvalReason).
- Around line 60-79: POWERSHELL_SAFE_READ_COMMANDS currently includes 'git',
which incorrectly treats all git invocations as read-only; remove 'git' from
POWERSHELL_SAFE_READ_COMMANDS and instead add explicit handling where commands
are analyzed: when the parsed command is 'git' inspect the first
subcommand/argument and only allow a curated whitelist of safe git subcommands
(e.g., status, rev-parse, log, show, diff, ls-files, branch -r, remote -v, tag
-l) to be treated as read-only; any other git subcommand (commit, reset, clean,
checkout, merge, rebase, etc.) must require approval or be routed to the
mutating branch. Update the logic that checks commands (the code that currently
consults POWERSHELL_SAFE_READ_COMMANDS) to call this git-specific validator so
git is not universally trusted.

In
`@workspaces/mi/mi-extension/src/ai-features/agent-mode/tools/shell_sandbox.ts`:
- Around line 97-127: The SAFE_READ_COMMANDS entry for "find" is unsafe because
it can perform mutations via flags like -exec, -execdir, or -delete; remove
"find" from the SAFE_READ_COMMANDS Set so any use of find requires approval, or
alternatively implement a helper (e.g., isFindMutation(tokens)) that inspects
the tokenized arguments for dangerous flags and include that check in
analyzeSegment's isMutation computation (refer to analyzeSegment, tokens, and
isMutation) so find remains allowed only when its arguments are confirmed
read-only.

In `@workspaces/mi/mi-extension/src/rpc-managers/agent-mode/rpc-manager.ts`:
- Around line 230-249: The addShellApprovalRule method currently allows errors
from persistShellApprovalRulesForSession to bubble up; wrap the await
this.persistShellApprovalRulesForSession(this.currentSessionId) call in a
try/catch, catch any error, log it via logInfo or a dedicated logger with
context (including this.currentSessionId and normalizedRule), and do not rethrow
so the in-memory this.shellApprovalRules remains and the agent flow/shell tool
callback is not disrupted; keep the existing push to this.shellApprovalRules
before persistence so the method still succeeds even if persistence fails.

In `@workspaces/mi/mi-extension/src/rpc-managers/ai-features/rpc-manager.ts`:
- Around line 666-691: The code reads currentState once via
StateMachineAI.state() and then may call
StateMachineAI.sendEvent(AI_EVENT_TYPE.*) later, which can race with other
callers; to fix, avoid using the stale local currentState: instead re-check
StateMachineAI.state() immediately before each send (for the UPDATE_USAGE,
USAGE_EXCEEDED and USAGE_RESET branches) and only call StateMachineAI.sendEvent
when the freshly read state still matches the expected state, or alternatively
send the event unconditionally but rely on the StateMachineAI guards/guards in
the state machine to ignore invalid transitions; update the logic around
StateMachineAI.state(), StateMachineAI.sendEvent, and the AI_EVENT_TYPE.* usages
accordingly so state decisions are based on a fresh read or delegated to the
state machine itself.

---

Outside diff comments:
In
`@workspaces/mi/mi-extension/src/ai-features/agent-mode/tools/data_mapper_tools.ts`:
- Around line 428-435: The code currently calls
undoCheckpointManager?.captureBeforeChange(path.relative(projectPath,
tsFilePath)) before validating the file contains mapFunction, which creates
unnecessary undo entries for invalid files; move the captureBeforeChange call so
it runs only after you verify content.includes('mapFunction') (i.e., after the
guard that returns the invalid-file error) to ensure checkpoints are only
created for files that will be modified; update references to
undoCheckpointManager.captureBeforeChange, mapFunction check, tsFilePath and
projectPath accordingly.

In
`@workspaces/mi/mi-extension/src/ai-features/agent-mode/tools/project_tools.ts`:
- Around line 312-318: The dependency object is using item.version?.tagName
which can be undefined and yield an invalid pom entry; update the code before
constructing dependencies (the block creating the dependencies:
DependencyDetails[] and the use of item.mavenGroupId/item.mavenArtifactId) to
validate that item.version?.tagName exists (or supply a safe default) and treat
missing version as an error or skip the dependency: e.g., check the same place
where Maven coordinates are validated (the earlier Maven check) and either
throw/log and return/continue when version is missing, or set version to a known
default string; ensure the dependency entry uses the validated non-undefined
version value instead of item.version?.tagName.

---

Nitpick comments:
In `@workspaces/mi/mi-extension/.env.example`:
- Around line 14-15: Reorder the two new environment keys so they follow the
file's alphabetical ordering: move MI_CONNECTOR_STORE_BACKEND_SUMMARIES to
appear before MI_COPILOT_ANTHROPIC_PROXY_URL, and move
MI_CONNECTOR_STORE_BACKEND_DETAILS_FILTER to appear before
MI_CONNECTOR_STORE_BACKEND_GETBYVERSION; update .env.example accordingly and
re-run dotenv-linter to confirm no ordering warnings remain.

In `@workspaces/mi/mi-extension/scripts/update-connector-context-db.js`:
- Around line 10-11: The SUMMARY_URL_TEMPLATE currently embeds an escaped
`${type}` token which yields a literal `${type}` and is later replaced, which is
confusing; change SUMMARY_URL_TEMPLATE to use a clear placeholder (e.g.
'TYPE_PLACEHOLDER') or make it a function like getSummaryUrl(type) that returns
`${API_BASE}/connectors/summaries?type=${type}&limit=100&offset=0&product=MI`;
then update fetchSummaries to call the new function or replace the new
placeholder instead of relying on the escaped `${type}` token (references:
SUMMARY_URL_TEMPLATE and fetchSummaries).

In `@workspaces/mi/mi-extension/src/ai-features/agent-mode/agents/main/tools.ts`:
- Line 118: Remove the duplicate import statement that brings in BashExecuteFn,
ToolResult, and ShellApprovalRuleStore and instead add these three symbols to
the existing import from '../../tools/types' (the earlier import block that
already imports other types from that module); ensure there are no duplicate
specifiers, keep the import list sorted/consistent with the project's style, and
run the linter/TS compile to confirm the redundant import is gone.

In
`@workspaces/mi/mi-extension/src/ai-features/agent-mode/tools/data_mapper_tools.ts`:
- Around line 409-417: The second call to resolveProjectBoundPath is redundant
because tsFilePath already holds its result in the earlier branch; remove the
duplicate call and simply keep the undefined guard/early-return that checks
tsFilePath (and returns the error object referencing dm_config_path), i.e.
delete the later resolveProjectBoundPath invocation and ensure the code uses the
existing tsFilePath variable and its null/undefined check to preserve the same
early-return behavior; references: resolveProjectBoundPath, tsFilePath,
dm_config_path.

In
`@workspaces/mi/mi-extension/src/ai-features/agent-mode/tools/file_edit_patch.ts`:
- Around line 145-163: The validateHunk function currently compares
FileEditHunk.old_text and .new_text with a raw string equality which can miss
cases where only normalization (trimming/trailing whitespace normalization or
line-ending normalization) makes them identical; update validateHunk to
normalize both old_text and new_text using the same normalization logic used
when matching/applying patches (the same routine that splits/normalizes lines
for matching) before performing the equality check so that identical content
after normalization is treated as identical; keep the existing error shape
(PatchApplyFailure) and the hunkIndex reference but use normalizedOld/new
variables when comparing in validateHunk.

In
`@workspaces/mi/mi-extension/src/ai-features/agent-mode/tools/plan_mode_tools.ts`:
- Around line 263-276: The pending plan-approval promise created in
requestPlanApproval (and stored in pendingApprovals / PendingPlanApproval) has
no timeout or session cleanup and can leak; add a timeout similar to
PendingQuestion using ASK_USER_TIMEOUT_MS: when creating the pending approval,
start a timer that rejects the promise with a timeout Error and deletes the
entry from pendingApprovals, and store/clear that timer so resolve/reject
callbacks clear it; also ensure cleanupPendingQuestionsForSession (or an
analogous cleanup function) removes any pendingApprovals for an abandoned
session to avoid leaks.

In
`@workspaces/mi/mi-extension/src/ai-features/agent-mode/tools/project_tools.ts`:
- Around line 257-268: The parameter resolvedItem in processItem is declared as
any|null; replace it with the concrete type from
ConnectorDefinitionLookupResult.definitionsByName (or a small union/alias that
matches that lookup's value, e.g. ConnectorDefinition or
ConnectorDefinitionLookupResult['definitionsByName'][keyof
ConnectorDefinitionLookupResult['definitionsByName']] | null) so callers get
compile-time shape checks; update the processItem signature and any call
sites/imports, and adjust any internal handling/optional checks in processItem
to match the new typed shape rather than using any.

In
`@workspaces/mi/mi-extension/src/ai-features/agent-mode/tools/shell_sandbox_powershell_ast.ts`:
- Around line 71-73: The current PARSER_TIMEOUT_MS (1500) is likely too tight
given powershell.exe cold-starts; increase PARSER_TIMEOUT_MS to a safer value
(e.g., 3000) or implement caching of the confirmed working binary to avoid
repeatedly trying the fallback chain in PARSER_BINARIES ('powershell.exe',
'pwsh') — update the parsing logic that iterates PARSER_BINARIES to store the
first successful binary and reuse it for subsequent parses to reduce cold-start
latency.

In `@workspaces/mi/mi-extension/src/ai-features/aiMachine.ts`:
- Around line 196-207: In the Unauthenticated state both
AI_EVENT_TYPE.COMPLETE_AUTH and AI_EVENT_TYPE.SIGN_IN_SUCCESS transition to
'Authenticated' and clear errorMessage; consolidate them by handling a single
event (e.g., map SIGN_IN_SUCCESS to COMPLETE_AUTH or collapse both into one
shared transition handler) or extract the shared transition into a named
transition/action used by both to avoid duplication; update the transition
definitions around the Unauthenticated state's handlers
(AI_EVENT_TYPE.COMPLETE_AUTH, AI_EVENT_TYPE.SIGN_IN_SUCCESS) and ensure
traceability by optionally adding a short comment indicating why events were
consolidated or kept distinct.

In `@workspaces/mi/mi-extension/src/rpc-managers/mi-diagram/rpc-manager.ts`:
- Around line 4926-4928: The API-unavailable branch in rpc-manager.ts returns {
hasComponent: hasContextYaml, isLoggedIn: false } but omits hasLocalChanges;
update that return to include hasLocalChanges: false so the DevantMetadata shape
is consistent (modify the branch that checks platformExtAPI and returns when
falsy to return { hasComponent: hasContextYaml, isLoggedIn: false,
hasLocalChanges: false }).

In `@workspaces/mi/mi-extension/src/test/suite/file-edit.patch.test.ts`:
- Around line 22-163: Add two new tests to the File Edit Patch Tests suite to
cover (1) a deletion hunk and (2) the equidistant line_hint edge case: create a
test calling applyStructuredFilePatch with a hunk where old_text is
'line_to_delete' and new_text is '' and assert success=true, newContent no
longer contains that line and splitReplacementLines behavior (empty array) is
respected; and create a test where the content has two identical matches
equidistant from a provided line_hint, call applyStructuredFilePatch with that
line_hint and assert the expected behavior (either success with deterministic
selection or success=false with code 'HUNK_AMBIGUOUS' per product decision).
Reference applyStructuredFilePatch and splitReplacementLines when adding
assertions so reviewers can validate the intended behavior and expected result
code.

In `@workspaces/mi/mi-visualizer/src/views/AIPanel/component/AIChatFooter.tsx`:
- Around line 1003-1008: The code currently sets pendingPlanApproval session
fields regardless of approval state; update the logic in AIChatFooter (the block
that assigns rememberForSession and suggestedPrefixRule from
pendingPlanApproval) so both fields are only populated when
pendingPlanApproval.approved is true and pendingPlanApproval.approvalKind ===
'shell_command'; in particular, set rememberForSession to
rememberShellApprovalForSession only when approved && approvalKind ===
'shell_command', and set suggestedPrefixRule only when approved && approvalKind
=== 'shell_command' && rememberShellApprovalForSession, using the same property
names (pendingPlanApproval.suggestedPrefixRule) to locate the change.

In
`@workspaces/mi/mi-visualizer/src/views/AIPanel/component/MICopilotContext.tsx`:
- Around line 40-45: Replace the inline union type used for the approvalKind
prop in MICopilotContext with the canonical PlanApprovalKind from `@wso2/mi-core`:
import PlanApprovalKind and change the approvalKind?: 'enter_plan_mode' | ...
declaration to approvalKind?: PlanApprovalKind, updating any references/usages
in MICopilotContext to the imported type so the UI stays in sync with the core
type and avoids duplication.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e4ce6e5 and 82c4ecd.

📒 Files selected for processing (44)
  • workspaces/mi/mi-core/src/rpc-types/agent-mode/types.ts
  • workspaces/mi/mi-extension/.env.example
  • workspaces/mi/mi-extension/scripts/update-connector-context-db.js
  • workspaces/mi/mi-extension/src/ai-features/agent-mode/agents/main/agent.ts
  • workspaces/mi/mi-extension/src/ai-features/agent-mode/agents/main/prompt.ts
  • workspaces/mi/mi-extension/src/ai-features/agent-mode/agents/main/system.ts
  • workspaces/mi/mi-extension/src/ai-features/agent-mode/agents/main/tools.ts
  • workspaces/mi/mi-extension/src/ai-features/agent-mode/chat-history-manager.ts
  • workspaces/mi/mi-extension/src/ai-features/agent-mode/context/connector_db.ts
  • workspaces/mi/mi-extension/src/ai-features/agent-mode/context/connectors_guide.ts
  • workspaces/mi/mi-extension/src/ai-features/agent-mode/context/inbound_db.ts
  • workspaces/mi/mi-extension/src/ai-features/agent-mode/context/synapse_expression_examples.ts
  • workspaces/mi/mi-extension/src/ai-features/agent-mode/context/synapse_expression_guide.ts
  • workspaces/mi/mi-extension/src/ai-features/agent-mode/context/synapse_guide.ts
  • workspaces/mi/mi-extension/src/ai-features/agent-mode/context/synapse_guide_old.ts
  • workspaces/mi/mi-extension/src/ai-features/agent-mode/stream_guard.ts
  • workspaces/mi/mi-extension/src/ai-features/agent-mode/tool-action-mapper.ts
  • workspaces/mi/mi-extension/src/ai-features/agent-mode/tools/bash_tools.ts
  • workspaces/mi/mi-extension/src/ai-features/agent-mode/tools/connector_store_cache.ts
  • workspaces/mi/mi-extension/src/ai-features/agent-mode/tools/connector_tools.ts
  • workspaces/mi/mi-extension/src/ai-features/agent-mode/tools/data_mapper_tools.ts
  • workspaces/mi/mi-extension/src/ai-features/agent-mode/tools/file_edit_patch.ts
  • workspaces/mi/mi-extension/src/ai-features/agent-mode/tools/file_tools.ts
  • workspaces/mi/mi-extension/src/ai-features/agent-mode/tools/plan_mode_tools.ts
  • workspaces/mi/mi-extension/src/ai-features/agent-mode/tools/project_tools.ts
  • workspaces/mi/mi-extension/src/ai-features/agent-mode/tools/shell_sandbox.ts
  • workspaces/mi/mi-extension/src/ai-features/agent-mode/tools/shell_sandbox_powershell.ts
  • workspaces/mi/mi-extension/src/ai-features/agent-mode/tools/shell_sandbox_powershell_ast.ts
  • workspaces/mi/mi-extension/src/ai-features/agent-mode/tools/subagent_tool.ts
  • workspaces/mi/mi-extension/src/ai-features/agent-mode/tools/types.ts
  • workspaces/mi/mi-extension/src/ai-features/agent-mode/tools/web_tools.ts
  • workspaces/mi/mi-extension/src/ai-features/aiMachine.ts
  • workspaces/mi/mi-extension/src/ai-features/auth.ts
  • workspaces/mi/mi-extension/src/ai-features/connection.ts
  • workspaces/mi/mi-extension/src/rpc-managers/agent-mode/rpc-manager.ts
  • workspaces/mi/mi-extension/src/rpc-managers/ai-features/rpc-manager.ts
  • workspaces/mi/mi-extension/src/rpc-managers/mi-diagram/rpc-manager.ts
  • workspaces/mi/mi-extension/src/test/suite/file-edit.patch.test.ts
  • workspaces/mi/mi-extension/src/test/suite/shell-sandbox.powershell.test.ts
  • workspaces/mi/mi-extension/src/test/suite/shell-sandbox.test.ts
  • workspaces/mi/mi-visualizer/src/views/AIPanel/component/AIChatFooter.tsx
  • workspaces/mi/mi-visualizer/src/views/AIPanel/component/AIChatHeader.tsx
  • workspaces/mi/mi-visualizer/src/views/AIPanel/component/MICopilotContext.tsx
  • workspaces/mi/mi-visualizer/src/views/AIPanel/utils.ts

Comment on lines +274 to +301
async function readExistingRecordsByName(filePath, exportName) {
const existing = await fs.readFile(filePath, 'utf8');
const exportIndex = existing.indexOf(`export const ${exportName} =`);
if (exportIndex < 0) {
throw new Error(`Could not find export declaration for ${exportName} in ${filePath}.`);
}

const arrayStart = existing.indexOf('[', exportIndex);
const arrayEnd = existing.lastIndexOf(']');
if (arrayStart < 0 || arrayEnd < 0 || arrayEnd < arrayStart) {
throw new Error(`Could not parse array contents from ${filePath}.`);
}

const parsed = JSON.parse(existing.slice(arrayStart, arrayEnd + 1));
if (!Array.isArray(parsed)) {
throw new Error(`Parsed existing data from ${filePath} is not an array.`);
}

const recordsByName = new Map();
for (const record of parsed) {
const name = getConnectorName(record);
if (name) {
recordsByName.set(name, record);
}
}

return recordsByName;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Fragile fallback: JSON.parse on TypeScript source will fail for common TS array syntax.

readExistingRecordsByName extracts a substring from a .ts file and passes it to JSON.parse (Line 287). TypeScript/JavaScript array literals routinely contain trailing commas, single-quoted strings, or comments — none of which are valid JSON. Any such pattern in connector_db.ts or inbound_db.ts will throw, crashing the entire fallback path and leaving the file unupdated instead of keeping the existing records.

Consider using JSON5, vm.runInNewContext, or a targeted regex/AST parse rather than raw JSON.parse on TS source.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@workspaces/mi/mi-extension/scripts/update-connector-context-db.js` around
lines 274 - 301, The current readExistingRecordsByName function is brittle
because it slices a TypeScript array literal and passes it to JSON.parse (the
JSON.parse call on the substring), which will throw on trailing commas, single
quotes or comments; replace that JSON.parse usage with a safer parser/evaluator
(e.g., JSON5.parse or evaluating the array literal in a sandboxed VM) so
TypeScript/JS syntax is accepted, and wrap the parse/eval in a try/catch to fall
back to returning an empty Map instead of crashing; update references in
readExistingRecordsByName and preserve the existing mapping logic that uses
getConnectorName to populate recordsByName.

Comment on lines +303 to +314
async function writeTsArrayFile(filePath, exportName, records) {
const existing = await fs.readFile(filePath, 'utf8');
const exportRegex = new RegExp(`^[\\s\\S]*?export const\\s+${exportName}\\s*=\\s*`);
const match = existing.match(exportRegex);

if (!match) {
throw new Error(`Could not find export declaration for ${exportName} in ${filePath}.`);
}

const content = `${match[0]}${JSON.stringify(records, null, 4)}\n`;
await fs.writeFile(filePath, content, 'utf8');
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

writeTsArrayFile silently truncates everything after the array (including the trailing semicolon and any subsequent declarations).

The regex on Line 305 matches from the start of file up to and including export const ${exportName} =, and the replacement is written as ${match[0]}${JSON.stringify(records)}\n with no trailing ;. This means:

  1. The written file will always lack a trailing semicolon, which will trigger lint/compiler warnings on every refresh.
  2. Any declarations that follow the array in the file (e.g., re-exports, helper constants) are permanently deleted on each run.

At minimum, append ; to the written content. If additional declarations may follow, capture and re-append the portion of the file after the closing ].

🐛 Minimal fix — preserve semicolon
-    const content = `${match[0]}${JSON.stringify(records, null, 4)}\n`;
+    const arrayEnd = existing.lastIndexOf(']');
+    const suffix = existing.slice(arrayEnd + 1); // preserves ';' and anything that follows
+    const content = `${match[0]}${JSON.stringify(records, null, 4)}${suffix.startsWith(';') ? suffix : ';\n'}`;
📝 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
async function writeTsArrayFile(filePath, exportName, records) {
const existing = await fs.readFile(filePath, 'utf8');
const exportRegex = new RegExp(`^[\\s\\S]*?export const\\s+${exportName}\\s*=\\s*`);
const match = existing.match(exportRegex);
if (!match) {
throw new Error(`Could not find export declaration for ${exportName} in ${filePath}.`);
}
const content = `${match[0]}${JSON.stringify(records, null, 4)}\n`;
await fs.writeFile(filePath, content, 'utf8');
}
async function writeTsArrayFile(filePath, exportName, records) {
const existing = await fs.readFile(filePath, 'utf8');
const exportRegex = new RegExp(`^[\\s\\S]*?export const\\s+${exportName}\\s*=\\s*`);
const match = existing.match(exportRegex);
if (!match) {
throw new Error(`Could not find export declaration for ${exportName} in ${filePath}.`);
}
const arrayEnd = existing.lastIndexOf(']');
const suffix = existing.slice(arrayEnd + 1); // preserves ';' and anything that follows
const content = `${match[0]}${JSON.stringify(records, null, 4)}${suffix.startsWith(';') ? suffix : ';\n'}`;
await fs.writeFile(filePath, content, 'utf8');
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@workspaces/mi/mi-extension/scripts/update-connector-context-db.js` around
lines 303 - 314, The writeTsArrayFile function currently overwrites everything
after the export declaration because exportRegex only matches up to "export
const <exportName> =" and the code writes match[0] + JSON without preserving the
trailing semicolon or any following content; update writeTsArrayFile to locate
the end of the array literal in the existing file (find the closing ] for the
exported array, include any trailing whitespace/comments and the existing
semicolon if present), then write back the prefix up through "export const
<exportName> =" + the new JSON.stringify(records, null, 4) + a semicolon and the
original suffix (everything after the closing ]), ensuring the regex/exportRegex
and match usage in writeTsArrayFile are adjusted to capture or compute the
suffix to preserve subsequent declarations.

Comment on lines 769 to 776
try {
const finalResponse = await response;
const finalResponse: any = response
? await awaitWithTimeout<any>(response, finalResponseWaitTimeoutMs)
: undefined;
finalModelMessages = finalResponse.messages || [];
} catch (error) {
logError('[Agent] Failed to capture model messages', error);
logError('[Agent] Failed to capture model messages on finish', error);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Null dereference when response is falsy or awaitWithTimeout times out.

When response is undefined (or null), the ternary on Line 770-772 evaluates to undefined, and Line 773 immediately accesses .messages on it — throwing a TypeError. The same pattern repeats at Lines 798-801 and 824-827.

The catch on Line 774 will swallow it, but in the finish case the intent is clearly to capture messages for a successful return, and silently losing them due to a preventable NPE is undesirable.

Proposed fix (apply to all three occurrences)
-                        const finalResponse: any = response
-                            ? await awaitWithTimeout<any>(response, finalResponseWaitTimeoutMs)
-                            : undefined;
-                        finalModelMessages = finalResponse.messages || [];
+                        const finalResponse: any = response
+                            ? await awaitWithTimeout<any>(response, finalResponseWaitTimeoutMs)
+                            : undefined;
+                        finalModelMessages = finalResponse?.messages || [];
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@workspaces/mi/mi-extension/src/ai-features/agent-mode/agents/main/agent.ts`
around lines 769 - 776, The code tries to read .messages from a possibly
undefined result of awaitWithTimeout, causing a null dereference; update each
occurrence (the block using awaitWithTimeout, finalResponseWaitTimeoutMs and
assigning finalModelMessages) to first await into a local const (e.g.,
finalResponse) and then safely access messages using optional chaining or a
fallback (e.g., finalResponse?.messages ?? []), so finalModelMessages is always
an array; apply the same safe-access pattern to the other two identical blocks
to avoid NPEs and keep the existing logError catch behavior.

Comment on lines +88 to 89
YOU ARE IN DEVELOPMENT PHASE. NOT IN PRODUCTION YET. HELP THE DEVELOPER IF DEVELOPER ASKS ABOUT YOUR INTERNALS/TOOL CALLS etc
{{system_remainder}}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Development-phase notice should not ship to production.

Line 88 instructs the model to reveal internals and tool calls. This contradicts the system prompt's own "Do not mention internal tool names to users" policy (system.ts Line 235) and the retrieved learning that system_reminder content must not be shown to end users. If this is intentionally temporary, consider gating it behind a debug/dev flag or adding a TODO to remove it before release.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@workspaces/mi/mi-extension/src/ai-features/agent-mode/agents/main/prompt.ts`
around lines 88 - 89, Remove or guard the hardcoded development notice string
("YOU ARE IN DEVELOPMENT PHASE...") in the prompt assembly so it is never sent
to production users; instead gate this message behind a debug/dev flag (e.g., a
config like isDevMode or process.env.DEBUG) or annotate it with a clear TODO to
delete before release. Specifically, update prompt.ts to stop injecting the
development string into the assembled prompt and ensure the variable
system_remainder is never exposed to end users (align with the non-disclosure
rule implemented in system.ts and its policy), and add a short unit/integration
check to confirm dev-only text is omitted unless debug mode is explicitly
enabled.

Comment on lines 238 to 246
<SYNAPSE_DEVELOPMENT_GUIDELINES>
${SYNAPSE_GUIDE}
</SYNAPSE_DEVELOPMENT_GUIDELINES>

<CONNECTOR_DEVELOPMENT_GUIDELINES>
${CONNECTOR_DOCUMENTATION}
</CONNECTOR_DEVELOPMENT_GUIDELINES>
`;
const SYSTEM_PROMPT_OLD = SYSTEM_PROMPT.replace(SYNAPSE_GUIDE, SYNAPSE_GUIDE_OLD);
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, find and examine the target file
cat -n workspaces/mi/mi-extension/src/ai-features/agent-mode/agents/main/system.ts | sed -n '230,255p'

Repository: wso2/vscode-extensions

Length of output: 1404


🏁 Script executed:

# Search for CONNECTOR_DOCUMENTATION definition
rg -n 'CONNECTOR_DOCUMENTATION\s*=' --type=ts -A 5

Repository: wso2/vscode-extensions

Length of output: 1814


🏁 Script executed:

# Search for the version-gated features mentioned
rg -n 'responseVariable|overwriteBody|connectionLocalEntryNeeded|noInitializationNeeded' --type=ts | head -30

Repository: wso2/vscode-extensions

Length of output: 4288


🏁 Script executed:

# Find where SYNAPSE_GUIDE and CONNECTOR_DOCUMENTATION are defined
rg -n 'SYNAPSE_GUIDE|CONNECTOR_DOCUMENTATION' --type=ts -B 2 -A 2 | head -50

Repository: wso2/vscode-extensions

Length of output: 5296


🏁 Script executed:

# Check the full CONNECTOR_DOCUMENTATION content
cat -n workspaces/mi/mi-extension/src/ai-features/agent-mode/context/connectors_guide.ts | sed -n '21,97p'

Repository: wso2/vscode-extensions

Length of output: 3838


🏁 Script executed:

# Search for version-specific information in connectors_guide.ts
rg -n 'version|4\.4|runtime|responseVariable|overwriteBody|connectionLocalEntryNeeded|noInitializationNeeded' workspaces/mi/mi-extension/src/ai-features/agent-mode/context/connectors_guide.ts

Repository: wso2/vscode-extensions

Length of output: 911


🏁 Script executed:

# Check if there's an old version of CONNECTOR_DOCUMENTATION
fd 'connectors_guide' --type=f

Repository: wso2/vscode-extensions

Length of output: 147


🏁 Script executed:

# Look at the agent-mode system.ts file more thoroughly to understand the version handling
cat -n workspaces/mi/mi-extension/src/ai-features/agent-mode/agents/main/system.ts | head -50

Repository: wso2/vscode-extensions

Length of output: 2215


🏁 Script executed:

# Check what happens with CONNECTOR_DOCUMENTATION in SYSTEM_PROMPT_OLD
cat -n workspaces/mi/mi-extension/src/ai-features/agent-mode/agents/main/system.ts | sed -n '1,100p'

Repository: wso2/vscode-extensions

Length of output: 7259


🏁 Script executed:

# Look for any OLD version of connector documentation
fd 'connectors' --type=f -e ts | xargs rg -l 'old|OLD|v1|version'

Repository: wso2/vscode-extensions

Length of output: 233


🏁 Script executed:

# Check the complete getSystemPrompt function to understand the version logic
cat -n workspaces/mi/mi-extension/src/ai-features/agent-mode/agents/main/system.ts | sed -n '248,260p'

Repository: wso2/vscode-extensions

Length of output: 420


🏁 Script executed:

# Search for RUNTIME_VERSION_440 to understand what it means
rg -n 'RUNTIME_VERSION_440' --type=ts -B 2 -A 2

Repository: wso2/vscode-extensions

Length of output: 44633


Create or use a version-appropriate connector documentation for older runtimes.

SYSTEM_PROMPT_OLD swaps SYNAPSE_GUIDESYNAPSE_GUIDE_OLD but retains the same CONNECTOR_DOCUMENTATION. The current documentation includes "Revamped response handling" features (responseVariable, overwriteBody) that are explicitly documented as supported only by certain connectors and newer runtimes. For MI runtime < 4.4.0, older code paths lack these features, making the guidance inapplicable. Either create CONNECTOR_DOCUMENTATION_OLD without the revamped response handling section, or conditionally render that section based on runtime version.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@workspaces/mi/mi-extension/src/ai-features/agent-mode/agents/main/system.ts`
around lines 238 - 246, SYSTEM_PROMPT_OLD currently substitutes SYNAPSE_GUIDE
with SYNAPSE_GUIDE_OLD but reuses CONNECTOR_DOCUMENTATION which documents
"Revamped response handling" (responseVariable, overwriteBody) not available for
older MI runtimes; fix by adding a version-appropriate connector doc (e.g.,
CONNECTOR_DOCUMENTATION_OLD) that omits the revamped response handling and use
it when building SYSTEM_PROMPT_OLD, or update the SYSTEM_PROMPT generation to
conditionally include the revamped section only when the runtime version >=
4.4.0 (check runtime version before choosing CONNECTOR_DOCUMENTATION vs
CONNECTOR_DOCUMENTATION_OLD); update references to SYSTEM_PROMPT_OLD,
SYNAPSE_GUIDE, SYNAPSE_GUIDE_OLD, and CONNECTOR_DOCUMENTATION to reflect the
chosen approach.

Comment on lines +60 to +79
const POWERSHELL_SAFE_READ_COMMANDS = new Set([
'cat',
'dir',
'get-childitem',
'get-content',
'get-item',
'get-location',
'get-process',
'git',
'ls',
'measure-object',
'pwd',
'resolve-path',
'select-string',
'sort-object',
'split-path',
'test-path',
'type',
'whoami',
]);
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Do not classify all git invocations as safe read-only.

Line 68 places git in the unconditional safe-read set. That lets mutating/destructive subcommands (for example git commit, git reset --hard, git clean) bypass approval in the read-only branch.

🔧 Minimal safe fix
 const POWERSHELL_SAFE_READ_COMMANDS = new Set([
     'cat',
     'dir',
@@
-    'git',
     'ls',

Also applies to: 804-813

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@workspaces/mi/mi-extension/src/ai-features/agent-mode/tools/shell_sandbox_powershell.ts`
around lines 60 - 79, POWERSHELL_SAFE_READ_COMMANDS currently includes 'git',
which incorrectly treats all git invocations as read-only; remove 'git' from
POWERSHELL_SAFE_READ_COMMANDS and instead add explicit handling where commands
are analyzed: when the parsed command is 'git' inspect the first
subcommand/argument and only allow a curated whitelist of safe git subcommands
(e.g., status, rev-parse, log, show, diff, ls-files, branch -r, remote -v, tag
-l) to be treated as read-only; any other git subcommand (commit, reset, clean,
checkout, merge, rebase, etc.) must require approval or be routed to the
mutating branch. Update the logic that checks commands (the code that currently
consults POWERSHELL_SAFE_READ_COMMANDS) to call this git-specific validator so
git is not universally trusted.

Comment on lines +707 to +710
const networkFromCommands = commandNames.some((commandName) => POWERSHELL_NETWORK_COMMANDS.has(commandName));
const networkFromDotNet = detectNetworkFromDotNet(trimmedCommand);
const networkDetected = networkFromCommands || networkFromDotNet;

Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Require approval when network access is detected.

On Line 804, networkDetected only bypasses the read-only allowlist check; it does not add any approval reason. As a result, commands like curl/Invoke-WebRequest can end up with requiresApproval=false (Line 824), which is a sandbox bypass.

🔧 Proposed fix
     const networkFromCommands = commandNames.some((commandName) => POWERSHELL_NETWORK_COMMANDS.has(commandName));
     const networkFromDotNet = detectNetworkFromDotNet(trimmedCommand);
     const networkDetected = networkFromCommands || networkFromDotNet;
+    if (networkDetected) {
+        reasons.push('Commands that access the network require approval.');
+    }

Also applies to: 804-815, 823-826

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@workspaces/mi/mi-extension/src/ai-features/agent-mode/tools/shell_sandbox_powershell.ts`
around lines 707 - 710, The code computes networkDetected using
POWERSHELL_NETWORK_COMMANDS and detectNetworkFromDotNet (based on commandNames
and trimmedCommand) but only bypasses the read-only allowlist check without
marking the action as requiring approval; update the approval logic so that when
networkDetected is true you set requiresApproval = true and append a clear
approvalReason (e.g., "network access detected via command/dotnet call") so
commands like curl/Invoke-WebRequest cannot slip through, and ensure this change
is applied wherever requiresApproval and approvalReason are set in the approval
flow handling (reference variables networkDetected, POWERSHELL_NETWORK_COMMANDS,
detectNetworkFromDotNet, requiresApproval, approvalReason).

Comment on lines +97 to +127
const SAFE_READ_COMMANDS = new Set([
'cat',
'cd',
'cut',
'dir',
'dirname',
'du',
'echo',
'env',
'find',
'git',
'grep',
'head',
'id',
'ls',
'pwd',
'readlink',
'realpath',
'rg',
'select-string',
'sort',
'stat',
'tail',
'tree',
'type',
'uniq',
'wc',
'where',
'which',
'whoami',
]);
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

find in SAFE_READ_COMMANDS can execute arbitrary mutations via -exec, -execdir, or -delete.

find is classified as a safe read-only command, but find /path -delete or find /path -exec rm {} \; performs destructive mutations. Since the per-segment analyzer doesn't inspect find's arguments for -exec/-delete/-execdir, these commands would pass through without approval.

Consider either removing find from SAFE_READ_COMMANDS (so it always requires approval) or adding argument-level detection for its mutation flags in analyzeSegment.

Minimal detection approach

Add a check in analyzeSegment (or a dedicated helper) for find's dangerous flags:

+function isFindMutation(tokens: string[]): boolean {
+    if (normalizeToken(tokens[0] || '') !== 'find') {
+        return false;
+    }
+    const dangerousFlags = ['-exec', '-execdir', '-delete', '-ok', '-okdir'];
+    return tokens.some((token) => dangerousFlags.includes(normalizeToken(token)));
+}

Then include isFindMutation(tokens) in the isMutation computation within analyzeSegment.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@workspaces/mi/mi-extension/src/ai-features/agent-mode/tools/shell_sandbox.ts`
around lines 97 - 127, The SAFE_READ_COMMANDS entry for "find" is unsafe because
it can perform mutations via flags like -exec, -execdir, or -delete; remove
"find" from the SAFE_READ_COMMANDS Set so any use of find requires approval, or
alternatively implement a helper (e.g., isFindMutation(tokens)) that inspects
the tokenized arguments for dangerous flags and include that check in
analyzeSegment's isMutation computation (refer to analyzeSegment, tokens, and
isMutation) so find remains allowed only when its arguments are confirmed
read-only.

Comment on lines +230 to +249
private async addShellApprovalRule(rule: string[]): Promise<void> {
if (!this.currentSessionId) {
return;
}

const normalizedRule = this.normalizeShellApprovalRule(rule);
if (normalizedRule.length === 0) {
return;
}

const existingKeys = new Set(this.shellApprovalRules.map((currentRule) => currentRule.join('\u0000')));
const ruleKey = normalizedRule.join('\u0000');
if (existingKeys.has(ruleKey)) {
return;
}

this.shellApprovalRules.push(normalizedRule);
await this.persistShellApprovalRulesForSession(this.currentSessionId);
logInfo(`[AgentPanel] Added shell approval rule for session ${this.currentSessionId}: ${normalizedRule.join(' ')}`);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Persistence error in addShellApprovalRule propagates to the agent tool, potentially disrupting execution.

If persistShellApprovalRulesForSession throws (e.g., disk full, permission error), the error bubbles up through the shellApprovalRuleStore.addRule() callback into the shell tool handler. The in-memory rule at Line 246 is already added, so only the disk persistence fails — consider catching and logging the error to keep the agent flow uninterrupted.

Proposed fix
         this.shellApprovalRules.push(normalizedRule);
-        await this.persistShellApprovalRulesForSession(this.currentSessionId);
+        try {
+            await this.persistShellApprovalRulesForSession(this.currentSessionId);
+        } catch (error) {
+            logError(`[AgentPanel] Failed to persist shell approval rule for session ${this.currentSessionId}`, error);
+        }
         logInfo(`[AgentPanel] Added shell approval rule for session ${this.currentSessionId}: ${normalizedRule.join(' ')}`);
📝 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
private async addShellApprovalRule(rule: string[]): Promise<void> {
if (!this.currentSessionId) {
return;
}
const normalizedRule = this.normalizeShellApprovalRule(rule);
if (normalizedRule.length === 0) {
return;
}
const existingKeys = new Set(this.shellApprovalRules.map((currentRule) => currentRule.join('\u0000')));
const ruleKey = normalizedRule.join('\u0000');
if (existingKeys.has(ruleKey)) {
return;
}
this.shellApprovalRules.push(normalizedRule);
await this.persistShellApprovalRulesForSession(this.currentSessionId);
logInfo(`[AgentPanel] Added shell approval rule for session ${this.currentSessionId}: ${normalizedRule.join(' ')}`);
}
private async addShellApprovalRule(rule: string[]): Promise<void> {
if (!this.currentSessionId) {
return;
}
const normalizedRule = this.normalizeShellApprovalRule(rule);
if (normalizedRule.length === 0) {
return;
}
const existingKeys = new Set(this.shellApprovalRules.map((currentRule) => currentRule.join('\u0000')));
const ruleKey = normalizedRule.join('\u0000');
if (existingKeys.has(ruleKey)) {
return;
}
this.shellApprovalRules.push(normalizedRule);
try {
await this.persistShellApprovalRulesForSession(this.currentSessionId);
} catch (error) {
logError(`[AgentPanel] Failed to persist shell approval rule for session ${this.currentSessionId}`, error);
}
logInfo(`[AgentPanel] Added shell approval rule for session ${this.currentSessionId}: ${normalizedRule.join(' ')}`);
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@workspaces/mi/mi-extension/src/rpc-managers/agent-mode/rpc-manager.ts` around
lines 230 - 249, The addShellApprovalRule method currently allows errors from
persistShellApprovalRulesForSession to bubble up; wrap the await
this.persistShellApprovalRulesForSession(this.currentSessionId) call in a
try/catch, catch any error, log it via logInfo or a dedicated logger with
context (including this.currentSessionId and normalizedRule), and do not rethrow
so the in-memory this.shellApprovalRules remains and the agent flow/shell tool
callback is not disrupted; keep the existing push to this.shellApprovalRules
before persistence so the method still succeeds even if persistence fails.

Comment on lines +666 to +691
// Get current state before updating
const currentState = StateMachineAI.state();

if (isUsageExceeded && currentState === 'Authenticated') {
logInfo('Quota exceeded. Transitioning to UsageExceeded state.');
StateMachineAI.sendEvent(AI_EVENT_TYPE.USAGE_EXCEEDED);
}
// Update usage via state machine event (proper XState pattern)
if (currentState === 'Authenticated' || currentState === 'UsageExceeded') {
StateMachineAI.sendEvent({ type: AI_EVENT_TYPE.UPDATE_USAGE, payload: { usage } });
}

// Check if we're in UsageExceeded state and if usage has reset
const isUsageReset = usage.remainingUsagePercentage > 0;
// Check if quota is exceeded and transition to UsageExceeded state
const isUnlimitedUsage = usage.remainingUsagePercentage === -1;
const isUsageExceeded = !isUnlimitedUsage && usage.remainingUsagePercentage <= 0;

if (currentState === 'UsageExceeded' && isUsageReset) {
logInfo('Usage has reset. Transitioning back to Authenticated state.');
StateMachineAI.sendEvent(AI_EVENT_TYPE.USAGE_RESET);
}
if (isUsageExceeded && currentState === 'Authenticated') {
logInfo('Quota exceeded. Transitioning to UsageExceeded state.');
StateMachineAI.sendEvent(AI_EVENT_TYPE.USAGE_EXCEEDED);
}

return usage;
// Check if we're in UsageExceeded state and if usage has reset
const isUsageReset = isUnlimitedUsage || usage.remainingUsagePercentage > 0;

if (currentState === 'UsageExceeded' && isUsageReset) {
logInfo('Usage has reset. Transitioning back to Authenticated state.');
StateMachineAI.sendEvent(AI_EVENT_TYPE.USAGE_RESET);
}

return usage;
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Race condition: state may change between StateMachineAI.state() read and sendEvent calls.

currentState is read at Line 667, but by the time sendEvent fires at Line 672/680/688, the state may have already changed (e.g., another concurrent fetchUsage call or a 429 in fetchWithAuth triggering USAGE_EXCEEDED). The window is small and the worst outcome is a redundant/ignored event, so this is low-risk, but worth noting.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@workspaces/mi/mi-extension/src/rpc-managers/ai-features/rpc-manager.ts`
around lines 666 - 691, The code reads currentState once via
StateMachineAI.state() and then may call
StateMachineAI.sendEvent(AI_EVENT_TYPE.*) later, which can race with other
callers; to fix, avoid using the stale local currentState: instead re-check
StateMachineAI.state() immediately before each send (for the UPDATE_USAGE,
USAGE_EXCEEDED and USAGE_RESET branches) and only call StateMachineAI.sendEvent
when the freshly read state still matches the expected state, or alternatively
send the event unconditionally but rely on the StateMachineAI guards/guards in
the state machine to ignore invalid transitions; update the logic around
StateMachineAI.state(), StateMachineAI.sendEvent, and the AI_EVENT_TYPE.* usages
accordingly so state decisions are based on a fresh read or delegated to the
state machine itself.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants