Skip to content

Conversation

@tysonthomas9
Copy link
Collaborator

@tysonthomas9 tysonthomas9 commented Dec 1, 2025

Summary by CodeRabbit

Release Notes

  • New Features

    • Introduced Agent Studio for creating, testing, and managing custom AI agents with persistent storage.
    • Added Mini Apps framework enabling users to launch and interact with mini applications within the AI Chat panel.
    • New tools for searching custom agents, calling agents from other agents, and saving research reports.
  • Improvements

    • Enhanced agent lifecycle management with improved initialization and refresh workflows.
    • Simplified final output handling and reporting mechanisms.

✏️ Tip: You can customize this high-level summary in your review settings.

@claude
Copy link

claude bot commented Dec 1, 2025

Claude finished @tysonthomas9's task —— View job


PR Review: Agent Builder

I've completed a comprehensive review of this large PR that introduces the "Agent Builder" functionality. Here's my detailed analysis:

📊 Overview

  • Size: 18,295 additions, 499 deletions across 70 files
  • Test Coverage: 58 test files covering 218 TypeScript files (~27% test coverage)
  • Architecture: Three major components - Agent Storage, Mini Apps Framework, and Agent Studio

Strengths

1. Architecture & Design

  • Well-structured modular design with clear separation of concerns
  • Singleton patterns used appropriately (AgentStorageManager, MiniAppRegistry)
  • Type safety throughout with comprehensive TypeScript interfaces
  • Event-driven architecture with proper event buses for mini apps

2. Code Quality

  • Consistent error handling with proper logging via centralized Logger
  • Security-conscious design - no direct eval/innerHTML usage found
  • Validation patterns - robust input validation in AgentStorageManager
  • Resource cleanup - proper disposal patterns in bridge uninstall methods

3. Testing

  • Comprehensive test coverage for core components (AgentRunner, MiniApp functionality)
  • Mock infrastructure well-designed with MockLLMClient for deterministic testing
  • Integration tests for complex workflows like agent handoffs

4. Mini Apps Framework

  • Innovative approach using Chrome DevTools Protocol for SPA communication
  • Single instance enforcement prevents resource conflicts
  • Proper lifecycle management with cleanup on app closure

⚠️ Areas for Improvement

1. Security Considerations

  • Runtime.evaluate usage: The mini apps framework uses Runtime.evaluate for DevTools→SPA communication. While necessary for the architecture, ensure:
    • Input sanitization on all data passed to Runtime.evaluate
    • Validate that user-provided agent configurations can't inject malicious code
  • Agent configuration validation: AgentStorageManager.ts:291-339 has good validation, but consider:
    • Sanitizing system prompts for XSS patterns
    • Limiting tool combinations that could be dangerous

2. Performance Considerations

  • Large SPA bundle: AgentStudioSPA.ts contains ~1,200 lines of embedded HTML/CSS/JS
    • Consider lazy loading or external resource files
    • Minification could reduce bundle size
  • IndexedDB queries: AgentStorageManager makes multiple DB calls during operations
    • Consider batching operations where possible
    • Add connection pooling for high-frequency operations

3. Error Handling

  • Bridge communication: Error handling in GenericMiniAppBridge.ts:76-100 could be improved
    • Add timeout handling for Runtime operations
    • Graceful degradation when CDP features are unavailable
  • Storage errors: Consider adding retry logic for IndexedDB operations

4. Code Maintenance

  • TODO items: Found 8 TODO comments, including:
    • AgentStudioMiniApp.ts:604 - "TODO: Implement agent testing"
    • Several debug console.log statements should be removed or moved to Logger
  • Magic strings: Consider constants for binding names and event types

🔍 Specific Issues Found

Minor Bugs

  1. AgentStorageManager.ts:281: The unregister comment mentions "ToolRegistry doesn't support unregistration" - this could lead to memory leaks
  2. MiniAppRegistry.ts:241: JavaScript wrapper could be extracted to a separate file for better maintainability

Type Safety

  1. SchemaProperty interfaces are duplicated between AgentStorageManager.ts and SchemaEditor.ts
  2. MiniAppTypes.ts: Some any types could be more specific

🎯 Recommendations

High Priority

  1. Security review: Audit all Runtime.evaluate calls and user input sanitization
  2. Performance optimization: Consider code-splitting the Agent Studio SPA
  3. Error resilience: Add timeout and retry logic for CDP operations

Medium Priority

  1. Consolidate types: Merge duplicate interfaces across files
  2. Remove debug code: Clean up console.log statements and TODO items
  3. Documentation: Add JSDoc for complex mini app communication protocol

Low Priority

  1. Test coverage: Aim for >40% coverage by adding tests for UI components
  2. Bundle optimization: Minify embedded SPA resources

📈 Overall Assessment

This is a high-quality, well-architected feature that significantly enhances the browser operator's capabilities. The code demonstrates:

  • Strong TypeScript practices
  • Thoughtful separation of concerns
  • Comprehensive testing for core functionality
  • Innovative use of CDP for mini app architecture

The mini apps framework is particularly impressive - it's a creative solution for embedding interactive UIs within DevTools while maintaining proper isolation and communication.

Recommendation: ✅ Approve with minor fixes

The identified issues are primarily minor and don't affect core functionality. The security considerations should be addressed, but the overall design is sound and ready for production with the suggested improvements.


@tysonthomas9 tysonthomas9 requested a review from olesho December 1, 2025 06:31
@coderabbitai
Copy link

coderabbitai bot commented Dec 1, 2025

Walkthrough

This PR introduces a comprehensive mini-apps framework for AI Chat, including Agent Storage Manager with IndexedDB persistence, Agent Studio Integration for custom agent management, and a full mini-app lifecycle system (registry, bridge, event bus, storage). It adds new tools (SaveResearchReport, SearchCustomAgents, CallCustomAgent, and six mini-app tools), updates BaseOrchestratorAgent to support custom agents, makes AgentRunner initialization async, and removes the prior structured response system. Extensive test coverage and UI components (Agent Studio View, Bridge, Schema Editor) complete the feature.

Changes

Cohort / File(s) Summary
Build Configuration
config/gni/devtools_grd_files.gni, front_end/panels/ai_chat/BUILD.gn
Added hundreds of bundled frontend sources for Agent Studio UI, settings, mini-apps infrastructure, tools, and providers; removed structured response files from public sources.
Agent Storage & Persistence
core/AgentStorageManager.ts, core/AgentStudioIntegration.ts
New IndexedDB-backed storage manager for CRUD operations on custom agents with validation and name indexing; integration module loads custom agents, registers them as tools, and aggregates with built-in agents for display.
Agent Testing Framework
core/AgentTestRunner.ts, core/__tests__/StateGraph.core.test.ts
New test runner with timeout and abort support; comprehensive test suite for StateGraph execution, routing, error handling, and abort signals.
Mini-Apps System Core
mini_apps/types/MiniAppTypes.ts, mini_apps/GenericMiniAppBridge.ts, mini_apps/MiniAppRegistry.ts, mini_apps/MiniAppEventBus.ts, mini_apps/MiniAppStorageManager.ts, mini_apps/MiniAppInitialization.ts
New framework defining MiniApp contracts, bidirectional CDP bridge communication, registry with launch/close lifecycle, singleton event bus with subscription filtering, per-app IndexedDB storage, and initialization orchestration.
Mini-Apps Agent Studio Implementation
mini_apps/apps/agent_studio/AgentStudioMiniApp.ts, mini_apps/apps/agent_studio/index.ts, mini_apps/index.ts
Agent Studio implementation as a MiniApp with action schema and state management; centralizes mini-apps exports.
Mini-Apps Tools
tools/mini_app/*.ts
Six new tools: ListMiniApps, LaunchMiniApp, GetMiniAppState, UpdateMiniAppState, ExecuteMiniAppAction, CloseMiniApp; enable agent-driven mini-app control.
Custom Agent Tools
tools/SaveResearchReportTool.ts, tools/SearchCustomAgentsTool.ts, tools/CallCustomAgentTool.ts
New tools for saving markdown reports, semantically searching custom agents, and invoking them; enables orchestrator to leverage custom agents.
Agent Framework Updates
agent_framework/AgentRunner.ts, agent_framework/implementation/ConfiguredAgents.ts, core/BaseOrchestratorAgent.ts
Force handoff terminationReason to 'handed_off'; make ConfiguredAgents async and initialize mini-app system; add custom agent tools to orchestrator configs.
UI Components
ui/AgentStudioBridge.ts, ui/AgentStudioController.ts, ui/AgentStudioView.ts, ui/SchemaEditor.ts, ui/agent_studio/AgentStudioSPA.ts
New Agent Studio UI components: bridge for SPA↔DevTools communication, controller for lifecycle and action handling, view wrapper with dual mini-app/legacy paths, schema editor for JSON schema visual editing, and bundled SPA HTML/CSS/JS.
UI Integration & Updates
ui/AIChatPanel.ts, ui/ChatView.ts
Add Agent Studio menu entry and view instantiation; remove structured response rendering and controller integration.
Testing Infrastructure
testing/MockLLMClient.ts, testing/TestUtilities.ts, testing/BUILD.gn
New mock LLM client with queue-based responses and test utilities factory; extensive test helper functions for messages, agents, and DOM queries.
Agent Framework Tests
agent_framework/__tests__/*.test.ts
Comprehensive suites for AgentRunner (core execution, handoff scenarios), ConfigurableAgentTool, ToolRegistry, and AgentRunnerEventBus.
Mini-Apps Tests
mini_apps/__tests__/*.test.ts
Unit tests for GenericMiniAppBridge, MiniAppEventBus, and MiniAppRegistry; validates lifecycle, event propagation, and SPA integration.
Mini-Apps Tools Tests
tools/mini_app/__tests__/MiniAppTools.test.ts
End-to-end tests for all six mini-app tools covering launch, state get/update, action execution, and close flows.
UI Component Tests
ui/__tests__/*.test.ts, ui/input/__tests__/*.test.ts, ui/message/__tests__/*.test.ts, ui/model_selector/__tests__/*.test.ts, ui/settings/__tests__/*.test.ts
Extensive test coverage for chat view, message components, input bar, model selector, and settings storage/validation.
Removed Modules
core/structured_response.ts, ui/message/StructuredResponseRender.ts, ui/message/StructuredResponseController.ts
Deleted structured response parsing, rendering, and controller logic; cleaned up related dependencies from configuration.
Constants & Settings Updates
core/Constants.ts, ui/settings/providerConfigs.ts
Removed REASONING_TAG and MARKDOWN_REPORT_TAG patterns; converted provider config string properties to lazy-evaluated getters.

Sequence Diagram(s)

sequenceDiagram
    participant DevTools as DevTools<br/>(AIChatPanel)
    participant Bridge as AgentStudio<br/>Bridge
    participant SPA as Agent Studio<br/>SPA
    participant Storage as Storage<br/>(IndexedDB)
    participant Framework as Agent<br/>Framework

    DevTools->>Bridge: onAction(handler)
    DevTools->>Bridge: install(webappId)
    Bridge->>SPA: Runtime.addBinding
    SPA->>SPA: window.__agentStudioBridge

    User->>SPA: Save Agent
    SPA->>Bridge: dispatch({type: 'save-agent'})
    Bridge->>DevTools: handleBindingCalled

    DevTools->>Storage: createAgent(config)
    Storage-->>DevTools: success/error

    DevTools->>Framework: AgentStudioIntegration.refreshAgents()
    Framework->>Storage: getAllAgents()
    Framework->>Framework: registerCustomAgent(stored)
    Framework-->>DevTools: agents registered

    DevTools->>Bridge: sendToSPA({action: 'agents-updated'})
    Bridge->>SPA: Runtime.evaluate
    SPA->>SPA: window.agentStudio.dispatch()
    SPA-->>User: Show updated agents
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Key areas requiring careful review:

  • Mini-apps system architecture — Event bus singleton, registry lifecycle, bridge CDP binding, and storage patterns; verify thread-safety and error recovery
  • IndexedDB initialization and transaction handling — Ensure proper async sequencing, error handling in AgentStorageManager and MiniAppStorageManager
  • Agent registration and configuration flow — Custom agent creation, registration in ToolRegistry, and handoff integration between orchestrator and custom agents
  • SPA↔DevTools communication protocol — AgentStudioBridge binding names, message parsing, iframe target resolution, and error scenarios
  • Test coverage completeness — Verify mocks accurately reflect real behavior, especially for async operations and error paths
  • Removal impact verification — Confirm structured response removal doesn't leave orphaned imports or unhandled rendering paths in ChatView and related components

Possibly related PRs

Suggested reviewers

  • olesho

🐰 Hops excitedly A mini-app meadow blooms! 🌱
With Agent Studios bright and storage deep,
Custom agents dance while frameworks leap,
Bridging DevTools with SPA's delight—
All together now in the framework's light! ✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 59.03% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Title check ❓ Inconclusive The title 'Agent Builder' is vague and does not clearly summarize the main changes. The PR introduces extensive Agent Studio infrastructure, mini-apps framework, custom agent management, new tools, and related testing—far broader than a generic 'Agent Builder' suggests. Clarify the title to reflect the primary scope: consider 'Add Agent Studio with Mini-App Framework and Custom Agent Management' or similar to better convey the feature's breadth.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/agent-improvements

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

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

try {
new URL(endpoint);
return true;

P1 Badge Reject non-http(s) endpoints in validation

validateEndpoint currently accepts any URL that parses, including ftp or other unsupported schemes, because it only calls new URL(endpoint) and returns true on success. The newly added validation tests expect ftp URLs to be rejected, and allowing them means users can save endpoints DevTools cannot call, causing configuration errors and failing the added tests. Restricting the protocol to http/https (and rejecting ftp) matches the intended behaviour.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Copy link

@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: 1

Note

Due to the large number of review comments, Critical severity comments were prioritized as inline comments.

🟠 Major comments (23)
front_end/panels/ai_chat/ui/model_selector/__tests__/ModelSelector.test.ts-308-310 (1)

308-310: Use assert.lengthOf for array length checks.

Consistent with the codebase style for checking array-like lengths.

Based on static analysis hints, apply this diff:

       const visibleOptions = selector.querySelectorAll('.model-option:not(.no-results)');
-      assert.strictEqual(visibleOptions.length, 2, 'Should only show GPT models');
+      assert.lengthOf(visibleOptions, 2, 'Should only show GPT models');
front_end/panels/ai_chat/ui/model_selector/__tests__/ModelSelector.test.ts-359-361 (1)

359-361: Use assert.lengthOf for array length checks.

Consistent with the codebase style for checking array-like lengths.

Based on static analysis hints, apply this diff:

       const visibleOptions = selector.querySelectorAll('.model-option:not(.no-results)');
-      assert.strictEqual(visibleOptions.length, 1);
+      assert.lengthOf(visibleOptions, 1);
front_end/panels/ai_chat/ui/model_selector/__tests__/ModelSelector.test.ts-74-76 (1)

74-76: Use assert.lengthOf for array length checks.

The codebase prefers assert.lengthOf for checking array-like lengths.

Based on static analysis hints, apply this diff:

       const optionEls = select!.querySelectorAll('option');
-      assert.strictEqual(optionEls.length, 3);
+      assert.lengthOf(optionEls, 3);
front_end/panels/ai_chat/ui/model_selector/__tests__/ModelSelector.test.ts-21-27 (1)

21-27: Fix type definition formatting.

Missing delimiters in the type definition according to the codebase style guide.

Based on static analysis hints, apply this diff:

   function createSelector(options?: {
-    options?: ModelOption[];
-    selected?: string;
-    disabled?: boolean;
-    preferAbove?: boolean;
-    forceSearchable?: boolean;
+    options?: ModelOption[],
+    selected?: string,
+    disabled?: boolean,
+    preferAbove?: boolean,
+    forceSearchable?: boolean,
   }): ModelSelector {
front_end/panels/ai_chat/ui/model_selector/__tests__/ModelSelector.test.ts-12-19 (1)

12-19: Use the preferred DOM rendering helper.

The ESLint rule flags direct DOM mutation. Consider using the renderElementIntoDOM helper if available in the codebase for better test isolation.

Based on static analysis hints, apply this pattern if renderElementIntoDOM is available:

-  beforeEach(() => {
-    container = document.createElement('div');
-    document.body.appendChild(container);
-  });
-
-  afterEach(() => {
-    document.body.removeChild(container);
-  });
+  beforeEach(() => {
+    container = renderElementIntoDOM('div');
+  });
+
+  afterEach(() => {
+    container.remove();
+  });

Committable suggestion skipped: line range outside the PR's diff.

front_end/panels/ai_chat/ui/settings/__tests__/validation.test.ts-11-15 (1)

11-15: Use interface and comma delimiters per project standards.

The static analysis correctly identifies style issues. Additionally, verify whether ModelOption is already exported from the validation module to avoid duplication.

Apply this diff to fix the style issues:

-type ModelOption = {
-  value: string;
-  label: string;
-  type?: string;
-};
+interface ModelOption {
+  value: string,
+  label: string,
+  type?: string,
+}

If ModelOption is already exported from validation.ts, import it instead:

-import {
-  validateModelSelection,
-  validateApiKey,
-  validateEndpoint,
-} from '../utils/validation.js';
-
-type ModelOption = {
-  value: string;
-  label: string;
-  type?: string;
-};
+import {
+  validateModelSelection,
+  validateApiKey,
+  validateEndpoint,
+  type ModelOption,
+} from '../utils/validation.js';
front_end/panels/ai_chat/ui/input/__tests__/InputBar.test.ts-23-64 (1)

23-64: Tighten typing for createInputBar options and avoid any casts

The helper currently uses (bar as any) for every property assignment, which triggers no-explicit-any and weakens type safety. The member-delimiter-style rule also expects commas rather than semicolons in the options type literal.

You can address both by (a) using an extended local type for the test and (b) switching delimiters to commas:

-  function createInputBar(options?: {
-    placeholder?: string;
-    disabled?: boolean;
-    sendDisabled?: boolean;
-    modelOptions?: Array<{value: string; label: string}>;
-    selectedModel?: string;
-    modelSelectorDisabled?: boolean;
-    currentProvider?: string;
-    centered?: boolean;
-    imageInput?: { url: string; bytesBase64: string };
-  }): InputBar {
-    const bar = document.createElement('ai-input-bar') as InputBar;
+  type TestInputBar = InputBar & {
+    placeholder?: string,
+    disabled?: boolean,
+    sendDisabled?: boolean,
+    modelOptions?: Array<{value: string; label: string}>,
+    selectedModel?: string,
+    modelSelectorDisabled?: boolean,
+    currentProvider?: string,
+    centered?: boolean,
+    imageInput?: { url: string; bytesBase64: string },
+  };
+
+  function createInputBar(options?: {
+    placeholder?: string,
+    disabled?: boolean,
+    sendDisabled?: boolean,
+    modelOptions?: Array<{value: string; label: string}>,
+    selectedModel?: string,
+    modelSelectorDisabled?: boolean,
+    currentProvider?: string,
+    centered?: boolean,
+    imageInput?: { url: string; bytesBase64: string },
+  }): InputBar {
+    const bar = document.createElement('ai-input-bar') as TestInputBar;
     if (options?.placeholder !== undefined) {
-      (bar as any).placeholder = options.placeholder;
+      bar.placeholder = options.placeholder;
     }
     if (options?.disabled !== undefined) {
-      (bar as any).disabled = options.disabled;
+      bar.disabled = options.disabled;
     }
     if (options?.sendDisabled !== undefined) {
-      (bar as any).sendDisabled = options.sendDisabled;
+      bar.sendDisabled = options.sendDisabled;
     }
     if (options?.modelOptions !== undefined) {
-      (bar as any).modelOptions = options.modelOptions;
+      bar.modelOptions = options.modelOptions;
     }
     if (options?.selectedModel !== undefined) {
-      (bar as any).selectedModel = options.selectedModel;
+      bar.selectedModel = options.selectedModel;
     }
     if (options?.modelSelectorDisabled !== undefined) {
-      (bar as any).modelSelectorDisabled = options.modelSelectorDisabled;
+      bar.modelSelectorDisabled = options.modelSelectorDisabled;
     }
     if (options?.currentProvider !== undefined) {
-      (bar as any).currentProvider = options.currentProvider;
+      bar.currentProvider = options.currentProvider;
     }
     if (options?.centered !== undefined) {
-      (bar as any).centered = options.centered;
+      bar.centered = options.centered;
     }
     if (options?.imageInput !== undefined) {
-      (bar as any).imageInput = options.imageInput;
+      bar.imageInput = options.imageInput;
     }

This keeps the helper strongly typed while satisfying the lint rules.

front_end/panels/ai_chat/ui/input/__tests__/InputBar.test.ts-14-21 (1)

14-21: Use renderElementIntoDOM instead of mutating document.body directly

The test setup/teardown currently appends/removes container directly on document.body, which violates the no-document-body-mutation lint rule and bypasses the common helper used elsewhere.

Recommend switching to renderElementIntoDOM and simplifying teardown, e.g.:

-import {raf} from '../../../../../testing/DOMHelpers.js';
+import {raf, renderElementIntoDOM} from '../../../../../testing/DOMHelpers.js';

  beforeEach(() => {
-    container = document.createElement('div');
-    document.body.appendChild(container);
+    container = document.createElement('div');
+    renderElementIntoDOM(container);
   });

   afterEach(() => {
-    document.body.removeChild(container);
+    container.remove();
   });

This keeps tests consistent with the shared DOM helpers and satisfies the lint rule.

Committable suggestion skipped: line range outside the PR's diff.

front_end/panels/ai_chat/ui/input/__tests__/ChatInput.test.ts-21-25 (1)

21-25: Fix member-delimiter-style violations in options type

ESLint’s @stylistic/member-delimiter-style is complaining on lines 22–24. The current inline type uses a delimiter style that doesn’t match the project configuration. Align with the configured style (commas for multiline members), e.g.:

-  function createInput(options?: {
-    placeholder?: string;
-    disabled?: boolean;
-    value?: string;
-  }): ChatInput {
+  function createInput(options?: {
+    placeholder?: string,
+    disabled?: boolean,
+    value?: string,
+  }): ChatInput {

This should clear the lint errors without changing runtime behavior.

front_end/panels/ai_chat/ui/input/__tests__/ChatInput.test.ts-303-322 (1)

303-322: Use assert.lengthOf and consider async behavior in rapid-input test

ESLint suggests using assert.lengthOf instead of checking .length manually. You can appease the linter and keep the intent clear:

-      assert.strictEqual(values.length, 20, 'Should capture all input events');
+      assert.lengthOf(values, 20, 'Should capture all input events');

Also, note that this test assumes inputchange is fired synchronously for each underlying input event. If ChatInput ever starts debouncing or batching these events, the expectations here will need to be revisited. For now, the current assertions are fine given synchronous behavior.

front_end/panels/ai_chat/ui/input/__tests__/ChatInput.test.ts-268-285 (1)

268-285: Fix unused initialHeight and consider strengthening auto-sizing assertion

ESLint flags initialHeight as assigned but never used. You can either remove it or use it to assert actual auto-sizing behavior. A tighter (but still reasonable) assertion would be:

-      const textarea = getTextarea(input);
-      const initialHeight = textarea.offsetHeight;
+      const textarea = getTextarea(input);
+      const initialHeight = textarea.offsetHeight;
@@
-      // Height should have increased (or at least not be the same single-line height)
-      // Note: exact heights depend on styling
-      assert.isNotNaN(textarea.offsetHeight);
+      // Height should increase for multi-line content.
+      const newHeight = textarea.offsetHeight;
+      assert.isAbove(newHeight, initialHeight, 'auto-sizing should increase height for multi-line content');

If you’re concerned about cross-environment flakiness, alternatively drop initialHeight entirely and keep a weaker assertion, but that won’t really validate auto-sizing semantics.

front_end/panels/ai_chat/ui/input/__tests__/ChatInput.test.ts-12-19 (1)

12-19: Use renderElementIntoDOM instead of direct document.body mutation

ESLint is flagging document.body.appendChild(container); tests generally should rely on the shared renderElementIntoDOM helper instead of mutating document.body directly. You can import it alongside raf and use it in beforeEach:

-import {raf} from '../../../../../testing/DOMHelpers.js';
+import {raf, renderElementIntoDOM} from '../../../../../testing/DOMHelpers.js';
@@
-  beforeEach(() => {
-    container = document.createElement('div');
-    document.body.appendChild(container);
-  });
+  beforeEach(() => {
+    container = document.createElement('div');
+    renderElementIntoDOM(container);
+  });

If renderElementIntoDOM already handles cleanup, you may also be able to drop the explicit removeChild in afterEach.

front_end/panels/ai_chat/ui/SchemaEditor.ts-432-437 (1)

432-437: Security: Avoid using '*' as postMessage targetOrigin.

Using '*' allows any origin to receive the message, which could leak schema data to malicious frames. Specify an explicit origin or use a more restrictive pattern.

 // Send to parent
 window.parent.postMessage({
   type: 'schema-change',
   schema: schema
-}, '*');
+}, window.location.origin);

Alternatively, if this is for cross-origin communication, document why '*' is necessary and consider validating the recipient.

front_end/panels/ai_chat/agent_framework/implementation/ConfiguredAgents.ts-18-20 (1)

18-20: Fix import order for consistency.

The new imports should be grouped with similar imports to follow the established import order pattern in this file.

Apply this diff to fix import ordering:

+import { SaveResearchReportTool } from '../../tools/SaveResearchReportTool.js';
+import { SearchCustomAgentsTool } from '../../tools/SearchCustomAgentsTool.js';
+import { CallCustomAgentTool } from '../../tools/CallCustomAgentTool.js';
 import { FetcherTool } from '../../tools/FetcherTool.js';
 import { FinalizeWithCritiqueTool } from '../../tools/FinalizeWithCritiqueTool.js';
 import { SchemaBasedExtractorTool } from '../../tools/SchemaBasedExtractorTool.js';
 import { StreamlinedSchemaExtractorTool } from '../../tools/StreamlinedSchemaExtractorTool.js';
 import { BookmarkStoreTool } from '../../tools/BookmarkStoreTool.js';
 import { DocumentSearchTool } from '../../tools/DocumentSearchTool.js';
 import { NavigateURLTool, PerformActionTool, GetAccessibilityTreeTool, SearchContentTool, NavigateBackTool, NodeIDsToURLsTool, TakeScreenshotTool, ScrollPageTool, WaitTool, RenderWebAppTool, GetWebAppDataTool, RemoveWebAppTool, CreateFileTool, UpdateFileTool, DeleteFileTool, ReadFileTool, ListFilesTool } from '../../tools/Tools.js';
 import { UpdateTodoTool } from '../../tools/UpdateTodoTool.js';
 import { ExecuteCodeTool } from '../../tools/ExecuteCodeTool.js';
 import { HTMLToMarkdownTool } from '../../tools/HTMLToMarkdownTool.js';
 import { ReadabilityExtractorTool } from '../../tools/ReadabilityExtractorTool.js';
+import { AgentStudioIntegration } from '../../core/AgentStudioIntegration.js';
 import { ConfigurableAgentTool, ToolRegistry } from '../ConfigurableAgentTool.js';
 import { ThinkingTool } from '../../tools/ThinkingTool.js';
-import { SaveResearchReportTool } from '../../tools/SaveResearchReportTool.js';
-import { SearchCustomAgentsTool } from '../../tools/SearchCustomAgentsTool.js';
-import { CallCustomAgentTool } from '../../tools/CallCustomAgentTool.js';
 import { registerMCPMetaTools } from '../../mcp/MCPMetaTools.js';
+import { initializeMiniApps } from '../../mini_apps/MiniAppInitialization.js';
 import { createDirectURLNavigatorAgentConfig } from './agents/DirectURLNavigatorAgent.js';
 import { createResearchAgentConfig } from './agents/ResearchAgent.js';
...
-import { AgentStudioIntegration } from '../../core/AgentStudioIntegration.js';
-import { initializeMiniApps } from '../../mini_apps/MiniAppInitialization.js';

Also applies to: 35-36

front_end/panels/ai_chat/ui/AIChatPanel.ts-93-94 (1)

93-94: Move import to the top with other UI imports.

The AgentStudioView import should be grouped with other UI imports at the top of the file (around lines 78-94) rather than placed in the middle after comments. This violates import ordering conventions.

Apply this diff to fix the import order:

 import { ChatView } from './ChatView.js';
 import { type ChatMessage, ChatMessageEntity, type ImageInputData, type ModelChatMessage, State as ChatViewState } from '../models/ChatTypes.js';
 import { HelpDialog } from './HelpDialog.js';
 import { SettingsDialog } from './SettingsDialog.js';
 import { EvaluationDialog } from './EvaluationDialog.js';
 import { MODEL_PLACEHOLDERS } from '../core/Constants.js';
 import * as Snackbars from '../../../ui/components/snackbars/snackbars.js';
+import { AgentStudioView } from './AgentStudioView.js';
 // MCP integration
 import { MCPRegistry } from '../mcp/MCPRegistry.js';
 import { getMCPConfig } from '../mcp/MCPConfig.js';
 import { onMCPConfigChange } from '../mcp/MCPConfig.js';
 import { MCPConnectorsCatalogDialog } from './mcp/MCPConnectorsCatalogDialog.js';
 // Conversation history
 import { ConversationHistoryList } from './ConversationHistoryList.js';
-// Agent Studio
-import { AgentStudioView } from './AgentStudioView.js';
front_end/panels/ai_chat/tools/SaveResearchReportTool.ts-104-109 (1)

104-109: Avoid using any type in catch block.

ESLint flags the use of any on line 104. Use unknown and proper type narrowing instead.

-    } catch (error: any) {
-      logger.error('Failed to save research report', { filename, error: error?.message });
+    } catch (error: unknown) {
+      const errorMessage = error instanceof Error ? error.message : String(error);
+      logger.error('Failed to save research report', { filename, error: errorMessage });
       return {
         success: false,
-        error: error?.message || 'Failed to save research report.'
+        error: errorMessage || 'Failed to save research report.'
       };
     }
front_end/panels/ai_chat/tools/SaveResearchReportTool.ts-5-8 (1)

5-8: Fix import ordering per ESLint rules.

Static analysis indicates import ordering issues. Regular imports should be grouped separately from type-only imports, and regular imports should come first within their group.

-import { createLogger } from '../core/Logger.js';
-import type { Tool, LLMContext } from './Tools.js';
 import { FileStorageManager } from './FileStorageManager.js';
 import { FileContentViewer } from '../ui/FileContentViewer.js';
+import { createLogger } from '../core/Logger.js';
+
+import type { Tool, LLMContext } from './Tools.js';
front_end/panels/ai_chat/mini_apps/MiniAppRegistry.ts-169-172 (1)

169-172: Partial state cleanup missing on launch failure.

If an error occurs after the webapp is rendered (e.g., during controller.initialize()), the webapp and bridge remain installed but the instance is not tracked, leaving orphaned resources.

Consider cleaning up partial state:

     } catch (error) {
       logger.error(`Failed to launch mini app "${appId}":`, error);
+      // Attempt cleanup of any partial initialization
+      try {
+        if (bridge?.installed) {
+          await bridge.uninstall();
+        }
+        if (webappId) {
+          const removeTool = new RemoveWebAppTool();
+          await removeTool.execute({ webappId, reasoning: `Cleanup after failed launch: ${appId}` });
+        }
+      } catch (cleanupError) {
+        logger.error(`Cleanup failed after launch error:`, cleanupError);
+      }
       throw error;
     }

This requires declaring bridge and webappId with let before the try block.

Committable suggestion skipped: line range outside the PR's diff.

front_end/panels/ai_chat/core/BaseOrchestratorAgent.ts-43-46 (1)

43-46: Fire-and-forget async initialization silently fails without error handling.

The initializeConfiguredAgents() function lacks any try-catch blocks and performs potentially failure-prone operations like registerMCPMetaTools(), initializeMiniApps(), and multiple tool factory registrations. Calling it with void means errors are unhandled, which may result in incomplete tool initialization without detection.

Add error logging:

 // Initialize configured agents (including custom agents from Agent Studio)
 // Note: This is async but we don't await here to avoid blocking module load.
 // Custom agents will be loaded asynchronously and available after initial load.
-void initializeConfiguredAgents();
+void initializeConfiguredAgents().catch(error => {
+  console.error('Failed to initialize configured agents:', error);
+});
front_end/panels/ai_chat/core/AgentTestRunner.ts-5-8 (1)

5-8: Tighten typing and address ESLint complaints (any, import order, member delimiters)

A few small changes will satisfy the lints and improve typings without behavior change:

  1. Remove explicit any from processResult (Line 183):
-  private processResult(result: ConfigurableAgentResult & { agentSession?: any }, state: TestExecutionState): AgentTestResult {
+  private processResult(result: ConfigurableAgentResult, state: TestExecutionState): AgentTestResult {

ConfigurableAgentTool.execute() can still return an object with extra fields; narrowing to ConfigurableAgentResult is valid and removes the no-explicit-any error.

  1. Fix inline type literals in convertChatMessage (Lines 223–231) to use commas instead of semicolons, matching project member-delimiter-style:
-    if (msg.entity === 'user') {
-      content = (msg as { text: string }).text || '';
-    } else if (msg.entity === 'model') {
-      const modelMsg = msg as { answer?: string; toolName?: string };
+    if (msg.entity === 'user') {
+      content = (msg as { text: string }).text || '';
+    } else if (msg.entity === 'model') {
+      const modelMsg = msg as { answer?: string, toolName?: string };
       content = modelMsg.answer || '';
       toolName = modelMsg.toolName;
     } else if (msg.entity === 'tool_result') {
-      const toolMsg = msg as { resultText: string; toolName: string };
+      const toolMsg = msg as { resultText: string, toolName: string };
       content = toolMsg.resultText || '';
       toolName = toolMsg.toolName;
     }
  1. Align imports with import/order + grouping rules (Lines 5–8). For example:
-import { createLogger } from './Logger.js';
-import { ConfigurableAgentTool, type AgentToolConfig, type ConfigurableAgentResult, type CallCtx } from '../agent_framework/ConfigurableAgentTool.js';
-import type { ChatMessage } from '../models/ChatTypes.js';
+import type { ChatMessage } from '../models/ChatTypes.js';
+
+import { createLogger } from './Logger.js';
+import { ConfigurableAgentTool, type AgentToolConfig, type ConfigurableAgentResult, type CallCtx } from '../agent_framework/ConfigurableAgentTool.js';

This should satisfy both the “import should occur after type import” and “empty line between import groups” ESLint errors.

Also applies to: 183-212, 217-233

front_end/panels/ai_chat/testing/TestUtilities.ts-10-16 (1)

10-16: Test helpers look solid; clean up lint issues (import order, unused type, style)

The helper implementations (mock tools, CallCtx/Agent configs, message builders, assertions) look consistent with the core types and appear correct for tests.

ESLint is still unhappy about a few things though:

  • Import/ordering + inline type imports: re‑order the type imports at the top so they satisfy import/order and keep related types together (e.g. AgentSessionTypes before ChatTypes if that’s the configured order).
  • Unused AgentMessage type (Line 13): drop it from the import list if you don’t end up using it anywhere.
  • member-delimiter-style & arrow-parens: several inline option types (e.g. function options params) use ; instead of ,, and arrow functions like messages.filter((m) => ...) still have parens around a single argument. Running the repo’s ESLint with --fix on this file should clean these up automatically.

No behavior changes are needed; this is purely to get the lints passing.

Also applies to: 24-31, 59-69, 147-164, 183-225, 288-354, 359-391, 400-417

front_end/panels/ai_chat/ui/__tests__/test-utils.ts-470-481 (1)

470-481: Fix setupChatView state updates and remove any cast

setupChatView currently:

  • Stores a fixed fullData snapshot (Line 511).
  • updateData always merges newData into that initial snapshot (Line 519), so later updates can unintentionally reset previously updated fields.
  • Uses (view as any).data, which triggers no-explicit-any.

You can address all three at once by tracking currentData and strongly typing the element:

 export interface ChatViewData {
   messages: ChatMessage[];
-  state: 'idle' | 'loading' | 'error';
+  state: 'idle' | 'loading' | 'error';
   isTextInputEmpty: boolean;
   onSendMessage: (text: string, imageInput?: ImageInputData | null) => void;
   onPromptSelected: (prompt: string, agentType?: string) => void;
   selectedAgent?: string;
   selectedModel?: string;
   modelOptions?: Array<{ value: string; label: string }>;
   onModelChanged?: (model: string) => void;
   isProcessing?: boolean;
 }
+
+type ChatViewElement = HTMLElement & { data: ChatViewData };
@@
 export async function setupChatView(data?: Partial<ChatViewData>): Promise<{
-  view: HTMLElement;
+  view: ChatViewElement;
   cleanup: () => void;
   updateData: (newData: Partial<ChatViewData>) => Promise<void>;
 }> {
-  const { element: view, cleanup } = renderToBody<HTMLElement>('devtools-chat-view');
-
-  const fullData = createChatViewData(data);
-  (view as any).data = fullData;
+  const { element: view, cleanup } = renderToBody<ChatViewElement>('devtools-chat-view');
+
+  let currentData = createChatViewData(data);
+  view.data = currentData;
   await raf();
@@
     view,
     cleanup,
     updateData: async (newData: Partial<ChatViewData>) => {
-      (view as any).data = createChatViewData({ ...fullData, ...newData });
+      currentData = createChatViewData({ ...currentData, ...newData });
+      view.data = currentData;
       await raf();
     },
   };
 }

This gives updateData proper incremental semantics and removes the explicit any.

Also applies to: 486-499, 504-523

front_end/panels/ai_chat/ui/__tests__/test-utils.ts-10-25 (1)

10-25: Align imports with project ESLint rules (es-modules-import, inline type imports, order)

The top‑level imports are tripping several custom ESLint rules:

  • rulesdir/es-modules-import: ../../../../testing/DOMHelpers.js should be imported using the project’s “namespace” pattern (e.g. import * as DOMHelpers from '...';) rather than pulling named exports directly. Mirror whatever pattern other tests in this repo use for DOMHelpers and then use DOMHelpers.raf / DOMHelpers.doubleRaf instead of named imports.
  • rulesdir/inline-type-imports: types and values from ChatTypes/AgentSessionTypes should be combined in a single import using the type keyword. For example:
-import {ChatMessageEntity, State} from '../../models/ChatTypes.js';
-import type {
-  ChatMessage,
-  UserChatMessage,
-  ModelChatMessage,
-  ToolResultMessage as ChatToolResultMessage,
-  AgentSessionMessage,
-  ImageInputData,
-} from '../../models/ChatTypes.js';
+import {
+  ChatMessageEntity,
+  State,
+  type ChatMessage,
+  type UserChatMessage,
+  type ModelChatMessage,
+  type ToolResultMessage as ChatToolResultMessage,
+  type AgentSessionMessage,
+  type ImageInputData,
+} from '../../models/ChatTypes.js';

And similarly for AgentSession, AgentMessage, ToolCallMessage, ToolResultMessage from AgentSessionTypes.

  • import/order: once combined, you may need to tweak the relative ordering of these imports to match the configured groups.

These are stylistic but enforced as errors, so worth fixing before landing.

Committable suggestion skipped: line range outside the PR's diff.

🟡 Minor comments (11)
front_end/panels/ai_chat/ui/ChatView.ts-596-596 (1)

596-596: Remove the any cast and narrow the type for renderModelMessage.

The explicit as any cast bypasses TypeScript type checking. While renderModelMessage only requires the base ModelChatMessage type (which includes action, answer, reasoning, and error), the modelMessage variable is typed as an intersection with additional properties needed for the tool call rendering path. Extract the base type before passing to renderModelMessage:

            if (isFinal) {
-              return renderModelMessage(modelMessage as any, this.#markdownRenderer);
+              const baseMessage: ModelChatMessage = modelMessage;
+              return renderModelMessage(baseMessage, this.#markdownRenderer);

This preserves type safety and clarifies that only the base type is needed for final answer rendering.

Committable suggestion skipped: line range outside the PR's diff.

front_end/panels/ai_chat/agent_framework/__tests__/ConfigurableAgentTool.test.ts-229-244 (1)

229-244: Test appears incomplete - descriptor is unused.

The test retrieves a descriptor but doesn't assert anything about it. Either add assertions or remove the unused assignment:

 it('registers agent descriptor on creation', async () => {
   const config = createMockAgentToolConfig({
     name: 'descriptor_agent',
     version: '1.0.0',
   });

   new ConfigurableAgentTool(config);

   // Wait for async registration
   await new Promise(resolve => setTimeout(resolve, 10));

   const descriptor = await AgentDescriptorRegistry.getDescriptor('descriptor_agent');
-  // Descriptor registration is async, may or may not be available immediately
-  // This test verifies the pattern is followed
+  // Verify descriptor was registered (may be null if async registration not complete)
+  // At minimum, this verifies no errors are thrown during registration
+  assert.isOk(true); // Placeholder - consider adding meaningful assertion
 });

Committable suggestion skipped: line range outside the PR's diff.

front_end/panels/ai_chat/ui/SchemaEditor.ts-10-16 (1)

10-16: SchemaProperty interface should use a distinct name to avoid future conflicts.

Three different SchemaProperty interfaces exist in the ai_chat module with incompatible structures:

  • SchemaEditor.ts:10: includes name and required fields
  • AgentStorageManager.ts:17: lacks name and required fields
  • SchemaBasedExtractorTool.ts:1341: different structure with format and recursive properties

While SchemaEditor.ts is currently isolated, this naming convention will create maintenance burden and potential conflicts if the file is integrated with other components. Rename this to EditorSchemaProperty or another distinct name.

front_end/panels/ai_chat/agent_framework/__tests__/AgentRunner.core.test.ts-667-690 (1)

667-690: LLM failure test doesn't restore original getInstance.

The test directly overwrites LLMClient.getInstance and sets mockLLMCleanup to an empty function, which doesn't restore the original implementation. This could leak state to subsequent tests.

Store and restore the original:

 it('terminates with error on LLM failure', async () => {
   // Make LLM throw an error
+  const originalGetInstance = (LLMClient as any).getInstance;
   (LLMClient as any).getInstance = () => ({
     call: async () => { throw new Error('LLM service unavailable'); },
     parseResponse: () => ({ type: 'error', error: 'n/a' }),
   });
-  mockLLMCleanup = () => {};
+  mockLLMCleanup = () => {
+    (LLMClient as any).getInstance = originalGetInstance;
+  };
front_end/panels/ai_chat/mini_apps/apps/agent_studio/AgentStudioMiniApp.ts-257-282 (1)

257-282: Unsafe type casting on action arguments.

The executeAction method uses aggressive type casting without validation, which can lead to runtime errors if the caller provides malformed arguments.

Consider validating arguments before casting or using a more defensive approach:

 async executeAction(actionName: string, args: unknown): Promise<unknown> {
-  const argsObj = args as Record<string, unknown>;
+  const argsObj = (args && typeof args === 'object') ? args as Record<string, unknown> : {};

   switch (actionName) {
     case 'select-agent':
+      if (typeof argsObj.name !== 'string') {
+        throw new Error('select-agent requires a string "name" argument');
+      }
       return this.handleSelectAgentAction(argsObj.name as string);
front_end/panels/ai_chat/agent_framework/__tests__/AgentRunner.core.test.ts-443-484 (1)

443-484: Potential race condition in abort test.

The test relies on a 20ms delay before aborting, which may be flaky on slower CI environments or under load. The slowTool has a 100ms delay, but timing-based tests can be unreliable.

Consider using a more deterministic approach:

-  // Create a slow tool that gives us time to abort
-  const slowTool = createMockTool('slow_tool', async () => {
-    await delay(100);
-    return { completed: true };
-  });
+  // Create a tool that waits for abort signal
+  let resolveToolExecution: () => void;
+  const toolPromise = new Promise<void>(resolve => { resolveToolExecution = resolve; });
+  const slowTool = createMockTool('slow_tool', async () => {
+    await toolPromise;
+    return { completed: true };
+  });
   // ... later, abort immediately after starting
+  controller.abort();
+  resolveToolExecution!(); // Allow tool to complete after abort

Committable suggestion skipped: line range outside the PR's diff.

front_end/panels/ai_chat/tools/mini_app/ListMiniAppsTool.ts-64-67 (1)

64-67: Confusing includeRunning filter logic.

When includeRunning === false, the code filters to only return apps that are NOT running. The naming and logic are counterintuitive—includeRunning: false means "exclude running apps" rather than "only return running apps."

Consider renaming for clarity:

 export interface ListMiniAppsArgs {
-  includeRunning?: boolean;
+  excludeRunning?: boolean;  // If true, exclude currently running apps
 }
-  // Filter to only running apps if requested
-  const result = args.includeRunning === false
-    ? apps.filter(app => !app.isRunning)
-    : apps;
+  // Filter out running apps if requested
+  const result = args.excludeRunning
+    ? apps.filter(app => !app.isRunning)
+    : apps;

Also update the schema description to match:

-  description: 'If false, only return apps that are not currently running. Default: true (include all apps).',
+  description: 'If true, exclude apps that are currently running. Default: false (include all apps).',

Committable suggestion skipped: line range outside the PR's diff.

front_end/panels/ai_chat/mini_apps/apps/agent_studio/AgentStudioMiniApp.ts-288-343 (1)

288-343: Type assertions in SPA action handling may cause runtime issues.

Lines 298, 309, and 324 use inline type assertions (action as SPAToDevToolsAction & {...}) without validating that the required properties exist. If the SPA sends malformed data, this will fail silently or throw cryptic errors.

Add property validation before accessing:

 case 'select-agent': {
-  const actionData = action as SPAToDevToolsAction & { name: string; id: string | null; isBuiltIn: boolean };
-  await this.handleSelectAgent(actionData.name, actionData.id, actionData.isBuiltIn);
+  const { name, id, isBuiltIn } = action as Record<string, unknown>;
+  if (typeof name !== 'string') {
+    logger.warn('select-agent action missing name property');
+    break;
+  }
+  await this.handleSelectAgent(name, id as string | null, Boolean(isBuiltIn));
   break;
 }
front_end/panels/ai_chat/mini_apps/MiniAppRegistry.ts-343-354 (1)

343-354: Potential duplicate 'ready' signal race condition.

Both the DOMContentLoaded listener and the document.readyState check can fire the 'ready' signal. If DOMContentLoaded fires during the 100ms delay of the second setTimeout, both signals will be sent.

Consider using a flag to ensure the ready signal is only sent once:

+  let __readySent = false;
+  function sendReady() {
+    if (__readySent) return;
+    __readySent = true;
+    window.${bindingName}(JSON.stringify({ type: 'ready' }));
+  }
+
   // Signal that mini app is ready
   window.addEventListener('DOMContentLoaded', function() {
-    setTimeout(function() {
-      window.${bindingName}(JSON.stringify({ type: 'ready' }));
-    }, 100);
+    setTimeout(sendReady, 100);
   });

   // If DOM is already loaded, signal ready immediately
   if (document.readyState !== 'loading') {
-    setTimeout(function() {
-      window.${bindingName}(JSON.stringify({ type: 'ready' }));
-    }, 100);
+    setTimeout(sendReady, 100);
   }
front_end/panels/ai_chat/core/AgentStudioIntegration.ts-277-285 (1)

277-285: Stale agents remain callable after "unregistration".

The unregisterCustomAgent method only removes the agent from the local registeredCustomAgents tracking Set (line 282) but cannot remove it from ToolRegistry since the registry has no public unregister capability. Agents registered via ToolRegistry.registerToolFactory() at line 268 persist in ToolRegistry's internal maps and remain callable despite being marked as unregistered locally.

Consider adding a TODO to track implementing proper unregistration support:

   private static unregisterCustomAgent(name: string): void {
     registeredCustomAgents.delete(name);
     logger.info(`Marked custom agent as unregistered: ${name}`);
-    // Note: Actual removal from ToolRegistry would require adding an unregister method
+    // TODO: Implement ToolRegistry.unregisterTool() to properly remove agents
+    // Currently deleted agents remain in ToolRegistry until page reload
   }
front_end/panels/ai_chat/core/AgentTestRunner.ts-131-143 (1)

131-143: Differentiate timeout from manual cancellation in error handling

Right now any aborted run is treated as a timeout (Line 135), even when cancelTest() is called. That produces misleading error text and terminationReason for user‑initiated cancels.

Consider tracking a manual‑cancel flag in the execution state and using it in the catch branch:

 interface TestExecutionState {
   abortController: AbortController;
   startTime: number;
   messages: TestMessage[];
-  iterations: number;
+  iterations: number;
+  cancelledManually: boolean;
 }
@@
   const state: TestExecutionState = {
     abortController: new AbortController(),
     startTime: Date.now(),
     messages: [],
-    iterations: 0,
+    iterations: 0,
+    cancelledManually: false,
   };
@@
   cancelTest(): boolean {
     if (this.currentTest) {
-      this.currentTest.abortController.abort();
+      this.currentTest.cancelledManually = true;
+      this.currentTest.abortController.abort();
       logger.info('Test execution cancelled');
       return true;
     }
@@
-      if (state.abortController.signal.aborted) {
-        return {
-          success: false,
-          error: 'Test execution timed out',
-          messages: state.messages,
-          iterations: state.iterations,
-          duration,
-          terminationReason: 'timeout',
-        };
-      }
+      if (state.abortController.signal.aborted) {
+        const terminationReason = state.cancelledManually ? 'cancelled' : 'timeout';
+        return {
+          success: false,
+          error: state.cancelledManually ? 'Test execution cancelled' : 'Test execution timed out',
+          messages: state.messages,
+          iterations: state.iterations,
+          duration,
+          terminationReason,
+        };
+      }

Also applies to: 164-171, 46-51, 73-80

Comment on lines +124 to +128
it('returns false for invalid URLs', () => {
assert.isFalse(validateEndpoint('not-a-url'));
assert.isFalse(validateEndpoint('just text'));
assert.isFalse(validateEndpoint('ftp://invalid-scheme.com')); // ftp is valid URL though...
});
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

# Locate the validateEndpoint implementation
find . -type f -name "*.ts" -o -name "*.js" | xargs grep -l "validateEndpoint" | head -20

Repository: BrowserOperator/browser-operator-core

Length of output: 218


🏁 Script executed:

# Search for the validateEndpoint function definition
rg "function validateEndpoint|export.*validateEndpoint|const validateEndpoint" -A 10

Repository: BrowserOperator/browser-operator-core

Length of output: 901


🏁 Script executed:

cat -n front_end/panels/ai_chat/ui/settings/__tests__/validation.test.ts | sed -n '124,128p'

Repository: BrowserOperator/browser-operator-core

Length of output: 376


🏁 Script executed:

# Check if there are any other references to scheme validation or restrictions in the codebase
rg "http|https|scheme" front_end/panels/ai_chat/ui/settings/ -i

Repository: BrowserOperator/browser-operator-core

Length of output: 4589


Fix the test assertion for FTP URLs or restrict scheme validation to HTTP/HTTPS.

The test at line 127 expects validateEndpoint('ftp://invalid-scheme.com') to return false, but the implementation accepts any valid URL scheme via the new URL() constructor. Since FTP is a valid URL scheme, this test assertion will fail. The inline comment even acknowledges the contradiction: "ftp is valid URL though..."

Either restrict the implementation to accept only HTTP/HTTPS schemes, or change the assertion to assert.isTrue if all valid URL schemes should be accepted.

🤖 Prompt for AI Agents
In front_end/panels/ai_chat/ui/settings/__tests__/validation.test.ts around
lines 124-128, the test expects validateEndpoint('ftp://invalid-scheme.com') to
be false but FTP is a valid URL so the assertion is incorrect; either change the
test to assert.isTrue for FTP if the validator should accept all URL schemes, or
update the validateEndpoint implementation to explicitly restrict allowed
schemes to http/https (and then keep the test as assert.isFalse); pick one
approach and make the corresponding change to the test and/or implementation so
tests and validation rules are consistent.

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