Skip to content

Initial implementation of automations frontend - #896

Open
larkox wants to merge 1 commit into
masterfrom
automationsFront
Open

Initial implementation of automations frontend#896
larkox wants to merge 1 commit into
masterfrom
automationsFront

Conversation

@larkox

@larkox larkox commented Jul 20, 2026

Copy link
Copy Markdown

Summary

Draft for the implementation of automations frontend.

Ticket Link

NONE

Screenshots

TBD

Release Note

TBD


Summary by CodeRabbit

  • New Features

    • Added an Automations section alongside Agents.
    • Create, edit, search, enable, disable, and delete automations.
    • Configure scheduled and event-based triggers, selected agents, prompts, channels, and context.
    • Added agent selection with avatars, metadata, and searchable options.
    • Added reusable configuration, filtering, selection, and row-action controls.
  • Improvements

    • Streamlined agent configuration and list interfaces.
    • Added updated labels and messages for automation and agent settings.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits.
Credits must be used to enable repository wide code reviews.

@larkox
larkox requested a review from matthewbirtch July 20, 2026 14:12
@mattermost-build

Copy link
Copy Markdown
Collaborator

Test check b16302a — action needed

PR introduces 17 frontend files with non-trivial pure utility functions and mock CRUD logic but no tests whatsoever.
Add unit tests for the pure utility functions in automation_config_view.tsx (buildTrigger, withTriggerChannelID, resolveInitialAgent) and the mock CRUD operations in client.tsx; these have real branching logic and are straightforward to test without rendering.

More details (truncated)
Test Files Detected
Category Count Files
Unit 0
Integration 0
E2E 0
Analysis

This draft PR adds an "Automations" frontend to the plugin. All 17 modified files are in webapp/src/ — there are no backend Go changes. No test files were added or modified.

New production logic introduced:

  1. Mock CRUD in client.tsxgetAutomations, getAutomation, createAutomation, updateAutomation, deleteAutomation operate on an in-memory mockAutomations array with non-trivial branching (find-by-id, slice splicing, 404 throws). These are pure async functions that are straightforwardly unit-testable.

  2. Pure utility functions in automation_config_view.tsx — Several functions carry real business logic with multiple branches:

    • buildTrigger(type, previous) — constructs a Trigger object depending on trigger type, carrying over channel/team/interval state.
    • withTriggerChannelID(trigger, channelId) — patches the correct nested field depending on trigger variant.
    • withScheduleInterval(trigger, interval) — guard on schedule presence.
    • withAgentId(automation, agentId) — maps over actions, calls ensureAIPromptAction.
    • withPrompt(automation, prompt) — similar mapping.
    • resolveInitialAgent(agents, agentId) — fallback logic (by id → first agent → null).
    • automationsEqual(a, b) — JSON-stringify equality.
  3. formatAgentMeta / channelAccessLabel in agent_selector.tsx — formatting helpers with a switch over ChannelAccessLevel variants; real branching that could silently regress.

  4. New React components (AutomationsList, AutomationRow, AutomationConfigView, ConfigViewShell, FilterTabs, TriggerSelector, RowActionsMenu, Avatar, AgentSelector, NewAutomationAgentMenu) — these are UI-heavy, but some contain state management logic worth exercising (e.g., the discard-changes flow in ConfigViewShell).

Mitigating factors:

  • The PR is explicitly a draft.
  • The client CRUD is explicitly marked "mocked until channel-automation is wired", so the implementation is intentionally temporary.
  • All changes are frontend-only; no Go business logic, no API endpoints added.

Even accounting for draft status, the pure utility functions (buildTrigger, withTriggerChannelID, resolveInitialAgent, formatAgentMeta, etc.) contain enough branching logic

...truncated. View full analysis details

Suggestions

  1. buildTrigger / withTriggerChannelID / withScheduleInterval — add unit tests in webapp/src/components/agents/__tests__/automation_config_view.test.ts covering each trigger type branch and the carry-over behavior (e.g., channel ID preserved when switching trigger types).
  2. resolveInitialAgent — test: empty list → null; matching ID → returns that agent; no match → returns first agent.
  3. formatAgentMeta / channelAccessLabel — test each ChannelAccessLevel variant and the MCP/tool count formatting edge cases.
  4. Mock CRUD (client.tsx) — test: create → getAll contains it; update non-existent → throws 404; delete → removes from list; getAutomation not found → throws 404. These are straightforward Jest tests with no render needed.
  5. ConfigViewShell discard-dialog flow — at minimum a render test verifying the dialog appears when isDirty=true and the back button is pressed.

To override, comment /test-analysis-override <reason> after verifying tests are adequate or not required.

@github-actions

Copy link
Copy Markdown

🤖 LLM Evaluation Results

OpenAI

⚠️ Overall: 22/28 tests passed (78.6%)

Provider Total Passed Failed Pass Rate
⚠️ OPENAI 28 22 6 78.6%

❌ Failed Evaluations

Show 6 failures

OPENAI

1. TestConversationMentionHandling/[openai]_conversation_from_attribution_long_thread.json

  • Score: 0.00
  • Rubric: is a list of bugs
  • Reason: The output does not provide a list of bugs; it states it cannot access bug reports and provides a blank template to be filled in later.

2. TestConversationMentionHandling/[openai]_conversation_from_attribution_long_thread.json

  • Score: 0.00
  • Rubric: includes a description of each bug
  • Reason: The output does not describe any specific bugs; it states it cannot access bug reports and provides a blank template. Therefore it does not include a description of each bug.

3. TestConversationMentionHandling/[openai]_conversation_from_attribution_long_thread.json

  • Score: 0.00
  • Rubric: attributes each bug to a user
  • Reason: The output provides a blank template and requests bug reports; it does not list any actual bugs or attribute any bug to a specific user.

4. TestConversationMentionHandling/[openai]_conversation_from_attribution_long_thread.json

  • Score: 0.00
  • Rubric: attributes the bug about trying to save without a color and the save button not doing anything to @maria.nunez
  • Reason: The output does not mention the specific bug (saving without a color / save button not doing anything) nor does it attribute that bug to @maria.nunez. It only provides a generic request for bug reports and a template.

5. TestConversationMentionHandling/[openai]_conversation_from_attribution_long_thread.json

  • Score: 0.00
  • Rubric: the bug about the end user being able to change channel banner is attributed to @maria.nunez
  • Reason: The output does not mention any specific bug about an end user being able to change the channel banner, and it does not attribute such a bug to @maria.nunez. It only provides a generic template and requests bug report details.

6. TestDirectMessageConversations/[openai]_bot_dm_tool_introspection

  • Score: 0.00
  • Rubric: mentions Github and refers to the documentation
  • Reason: The output refers to documentation (docs.mattermost.com) but does not mention GitHub anywhere. Since the rubric requires both mentioning GitHub and referring to the documentation, it fails.

Anthropic

⚠️ Overall: 22/28 tests passed (78.6%)

Provider Total Passed Failed Pass Rate
⚠️ ANTHROPIC 28 22 6 78.6%

❌ Failed Evaluations

Show 6 failures

ANTHROPIC

1. TestConversationMentionHandling/[anthropic]_conversation_from_attribution_long_thread.json

  • Score: 0.00
  • Rubric: is a list of bugs
  • Reason: The output does not provide a list of bugs; it states the assistant lacks access to bug reports and suggests ways to search for them, asking the user to paste details.

2. TestConversationMentionHandling/[anthropic]_conversation_from_attribution_long_thread.json

  • Score: 0.00
  • Rubric: includes a description of each bug
  • Reason: The output states it cannot access bug reports and suggests how to search, but it does not provide any descriptions of bugs. Therefore it does not include a description of each bug.

3. TestConversationMentionHandling/[anthropic]_conversation_from_attribution_long_thread.json

  • Score: 0.00
  • Rubric: attributes each bug to a user
  • Reason: The output states it cannot access bug reports and suggests searching, but it does not list any bugs and does not attribute any bug to a user.

4. TestConversationMentionHandling/[anthropic]_conversation_from_attribution_long_thread.json

  • Score: 0.00
  • Rubric: attributes the bug about trying to save without a color and the save button not doing anything to @maria.nunez
  • Reason: The output does not mention @maria.nunez and does not attribute the specific bug about saving without a color (save button doing nothing) to her. It only states lack of access and suggests how to search.

5. TestConversationMentionHandling/[anthropic]_conversation_from_attribution_long_thread.json

  • Score: 0.00
  • Rubric: the bug about the end user being able to change channel banner is attributed to @maria.nunez
  • Reason: The output does not attribute the bug about an end user being able to change the channel banner to @maria.nunez; it only states lack of access and suggests searching/formatting.

6. TestDirectMessageConversations/[anthropic]_bot_dm_tool_introspection

  • Score: 0.00
  • Rubric: mentions Github and refers to the documentation
  • Reason: The output refers to documentation (docs.mattermost.com) but does not mention GitHub, which is required by the rubric.

This comment was automatically generated by the eval CI pipeline.

@coderabbitai

coderabbitai Bot commented Jul 20, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds a complete automation interface with typed models, mocked CRUD operations, list filtering, creation and editing flows, trigger and agent selectors, reusable configuration shells, and integration into the agents page.

Changes

Automation management

Layer / File(s) Summary
Shared contracts and UI primitives
webapp/src/types/automations.ts, webapp/src/components/select.tsx, webapp/src/components/agents/config_view_shell.tsx, webapp/src/components/agents/avatar.tsx, webapp/src/components/agents/row_actions_menu.tsx, webapp/src/components/agents/filter_tabs.tsx
Adds typed automation models and helpers, reusable select styling and single-select controls, configuration-shell behaviour, avatars, row actions, and ownership filter tabs.
Automation data and list flow
webapp/src/client.tsx, webapp/src/components/agents/automations_list.tsx, webapp/src/components/agents/automation_row.tsx, webapp/src/i18n/en.json
Adds in-memory automation CRUD, loading and filtering, profile and agent resolution, row-level enable/edit/delete actions, and automation-related translations.
Automation configuration and selection
webapp/src/components/agents/automation_config_view.tsx, webapp/src/components/agents/trigger_selector.tsx, webapp/src/components/agents/agent_selector.tsx, webapp/src/components/agents/new_automation_agent_menu.tsx
Adds automation draft editing, trigger and schedule configuration, agent selection, prompt updates, and new-automation agent picking.
Agents page integration and refactoring
webapp/src/components/agents/agents_page.tsx, webapp/src/components/agents/agents_list.tsx, webapp/src/components/agents/agent_config_view.tsx, webapp/src/components/agents/agent_row.tsx
Adds agents and automations page tabs, adopts reusable ownership filters and row menus, and delegates agent configuration layout to the shared shell.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant AgentsPage
  participant AutomationsList
  participant AutomationConfigView
  participant ClientCRUD
  User->>AgentsPage: select Automations
  AgentsPage->>AutomationsList: render list
  AutomationsList->>ClientCRUD: getAutomations and getAgents
  ClientCRUD-->>AutomationsList: return data
  User->>AutomationsList: create or edit automation
  AutomationsList->>AutomationConfigView: render configuration
  User->>AutomationConfigView: save changes
  AutomationConfigView->>AutomationsList: return AutomationUpdate
  AutomationsList->>ClientCRUD: createAutomation or updateAutomation
  ClientCRUD-->>AutomationsList: return saved automation
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and accurately summarizes the main change: the initial frontend implementation for automations.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch automationsFront

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 Checkov (3.3.8)
webapp/src/i18n/en.json

Traceback (most recent call last):
File "/usr/local/bin/checkov", line 2, in
from checkov.main import Checkov
ModuleNotFoundError: No module named 'checkov'


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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (9)
webapp/src/components/agents/agent_selector.tsx (1)

25-40: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicated MCP/tools meta-formatting logic across two files. Both formatAgentMeta and formatPickerMeta independently recompute the same mcpCount (distinct server_origin count) and toolsLabel (all-tools vs. pluralized count) derivation from UserAgent, differing only in the join separator (· vs ).

  • webapp/src/components/agents/agent_selector.tsx#L25-L40: extract the mcpCount/toolsLabel computation into a shared helper (e.g., webapp/src/types/agents.ts or a new agent_meta.ts) and have formatAgentMeta call it.
  • webapp/src/components/agents/new_automation_agent_menu.tsx#L24-L46: have formatPickerMeta call the same shared helper instead of recomputing mcpCount/toolsLabel locally.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@webapp/src/components/agents/agent_selector.tsx` around lines 25 - 40, The
MCP/tool metadata derivation is duplicated between formatAgentMeta and
formatPickerMeta. Create a shared helper for distinct server_origin counting and
the all-tools versus pluralized tools label, then update formatAgentMeta in
webapp/src/components/agents/agent_selector.tsx:25-40 and formatPickerMeta in
webapp/src/components/agents/new_automation_agent_menu.tsx:24-46 to use it while
preserving their different separators and channel labels.
webapp/src/components/agents/new_automation_agent_menu.tsx (2)

214-216: 🎯 Functional Correctness | 🔵 Trivial

Author-flagged TODO: description fallback needs to change before merging.

Want me to help wire in a real description field (or confirm the intended fallback) before this leaves draft?

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@webapp/src/components/agents/new_automation_agent_menu.tsx` around lines 214
- 216, Update the agent display logic surrounding the fallback return in the new
automation agent menu to use the agent’s real description field instead of
custom instructions. If the description field is not yet available, confirm and
implement the intended replacement fallback before merging, and remove the
temporary TODO.

156-198: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Dropdown ARIA/keyboard gaps.

role='listbox' is applied to a container whose direct children include a header and a search <input>, not just options — this breaks the expected ARIA listbox structure for assistive tech (e.g., wrap only the option items in the listbox role, or use combobox with an owned listbox). Also, there's no Escape key handling to close the dropdown (only click-outside).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@webapp/src/components/agents/new_automation_agent_menu.tsx` around lines 156
- 198, The dropdown in the open state has an invalid listbox structure and lacks
Escape handling. Update the JSX around Dropdown and OptionsList so the listbox
role applies only to the agent options (or implement the equivalent combobox
with an owned listbox), keeping the header and SearchInput outside that role;
add keyboard handling tied to the existing open/close state so pressing Escape
closes the dropdown and preserves the current focus behavior.
webapp/src/components/agents/automations_list.tsx (2)

86-95: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use the AIProviderTypeAgent constant instead of the literal 'agent'.

@/types/automations exports AIProviderTypeAgent for this exact purpose, and it's already used the same way in automation_config_view.tsx's ensureAIPromptAction. Hardcoding 'agent' here risks drift/typos.

♻️ Proposed fix
 import {
     Automation,
     AutomationUpdate,
+    AIProviderTypeAgent,
     getAIPromptAction,
     getTriggerType,
 } from '`@/types/automations`';
...
             {
                 id: 'run-agent',
                 ai_prompt: {
                     prompt: '',
-                    provider_type: 'agent',
+                    provider_type: AIProviderTypeAgent,
                     provider_id: agent.id,
                 },
             },
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@webapp/src/components/agents/automations_list.tsx` around lines 86 - 95, In
the automation action configuration, update the provider_type value within the
run-agent action to use the exported AIProviderTypeAgent constant from
`@/types/automations` instead of the literal 'agent'. Preserve the surrounding
action and prompt configuration.

137-171: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Missing-profile fetch re-runs on every unrelated automations-state change.

This effect depends on automations (a new array reference on every toggle/edit/delete/create) and profiles. If any created_by id never resolves (e.g. deleted user), it will stay in missingIds forever and this effect re-fetches it on every unrelated list mutation, with no memoization of ids already attempted.

♻️ Proposed fix (track attempted ids)
+    const attemptedIdsRef = useRef<Set<string>>(new Set());
+
     useEffect(() => {
         const missingIds = [...new Set(
             automations.
                 map((item) => item.created_by).
-                filter((id): id is string => Boolean(id) && !profiles[id]),
+                filter((id): id is string => Boolean(id) && !profiles[id] && !attemptedIdsRef.current.has(id)),
         )];
         if (missingIds.length === 0) {
             return () => {
                 // No profiles to fetch
             };
         }
+        missingIds.forEach((id) => attemptedIdsRef.current.add(id));
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@webapp/src/components/agents/automations_list.tsx` around lines 137 - 171,
Update the profile-fetching useEffect and its surrounding state in
automations_list.tsx to track creator IDs already attempted, using a stable
attempted-ID set/ref so unresolved IDs are not fetched again after unrelated
automations or profiles changes. Build missingIds by excluding both cached
profiles and attempted IDs, mark IDs as attempted before calling
getProfilesByIds, and preserve the existing cancellation and RECEIVED_PROFILES
dispatch behavior.
webapp/src/components/agents/row_actions_menu.tsx (1)

29-42: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Dropdown doesn't close on Escape.

Only an outside-mousedown listener closes the menu; keyboard-only users have no way to dismiss it via Escape.

♻️ Proposed fix
     useEffect(() => {
         if (!open) {
             return () => {
                 // No mousedown listener while menu is closed
             };
         }
         const handler = (e: MouseEvent) => {
             if (menuRef.current && !menuRef.current.contains(e.target as Node)) {
                 updateOpen(false);
             }
         };
+        const keyHandler = (e: KeyboardEvent) => {
+            if (e.key === 'Escape') {
+                updateOpen(false);
+            }
+        };
         document.addEventListener('mousedown', handler);
-        return () => document.removeEventListener('mousedown', handler);
+        document.addEventListener('keydown', keyHandler);
+        return () => {
+            document.removeEventListener('mousedown', handler);
+            document.removeEventListener('keydown', keyHandler);
+        };
     }, [open, updateOpen]);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@webapp/src/components/agents/row_actions_menu.tsx` around lines 29 - 42, The
open-menu effect in the row actions component only handles outside mousedown
events. Add a keydown listener while open that calls updateOpen(false) when the
Escape key is pressed, and remove that listener during cleanup alongside the
existing mousedown listener.
webapp/src/components/agents/config_view_shell.tsx (1)

111-128: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Both tab strips render plain buttons without ARIA tab semantics. Neither ConfigViewShell's section tabs nor FilterTabs' ownership tabs expose role="tablist"/"tab" or aria-selected, so screen-reader users don't get standard tab navigation semantics.

  • webapp/src/components/agents/config_view_shell.tsx#L111-L128: add role="tablist" to TabsContainer and role="tab" + aria-selected={activeTabId === tab.id} to each TabButton.
  • webapp/src/components/agents/filter_tabs.tsx#L25-L42: add role="tablist" to TabBar and role="tab" + aria-selected={value === 'all'/'yours'} to each TabButton.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@webapp/src/components/agents/config_view_shell.tsx` around lines 111 - 128,
Add tab semantics to both tab strips: in
webapp/src/components/agents/config_view_shell.tsx lines 111-128, give
TabsContainer role="tablist" and each TabButton role="tab" with aria-selected
reflecting activeTabId === tab.id; in
webapp/src/components/agents/filter_tabs.tsx lines 25-42, give TabBar
role="tablist" and each ownership TabButton role="tab" with aria-selected
reflecting whether value is "all" or "yours".
webapp/src/types/automations.ts (1)

15-16: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

provider_type should use the exported literal constants as a type, not string.

AIProviderTypeAgent/AIProviderTypeService are defined but provider_type is typed as plain string (comment-only documentation of the two valid values). This allows any string to type-check, defeating the purpose of the constants and the strict-typing guideline.

♻️ Proposed fix
 export const AIProviderTypeAgent = 'agent';
 export const AIProviderTypeService = 'service';
+export type AIProviderType = typeof AIProviderTypeAgent | typeof AIProviderTypeService;
 
 export type AIPromptActionParams = {
     system_prompt?: string;
     prompt: string;
-    provider_type: string; // "agent" | "service"
+    provider_type: AIProviderType;
     provider_id: string;
     allowed_tools?: string[];
     guardrails?: Guardrails;
     request_as?: AIPromptRequestAs;
 };
As per coding guidelines, "keep TypeScript strictly typed" for `webapp/**/*.{ts,tsx}`.

Also applies to: 69-77

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@webapp/src/types/automations.ts` around lines 15 - 16, Update the automation
type containing provider_type to use the literal union typeof
AIProviderTypeAgent | typeof AIProviderTypeService instead of string. Reuse the
exported constants AIProviderTypeAgent and AIProviderTypeService so only the two
supported provider values are accepted.

Source: Coding guidelines

webapp/src/components/agents/agents_page.tsx (1)

42-59: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider adding tab ARIA semantics. The tab bar uses plain buttons; adding role="tablist" on the container and role="tab" + aria-selected={$active} on each PageTabButton (with the panel wired via role="tabpanel"/aria-labelledby) would let assistive tech announce this as a tab set. Functionally correct as-is.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@webapp/src/components/agents/agents_page.tsx` around lines 42 - 59, Add ARIA
tab semantics around the existing activeTab controls: apply role="tablist" to
TabsBar, role="tab" and aria-selected based on $active to each PageTabButton,
and associate the rendered tab panel with the selected tab using role="tabpanel"
and aria-labelledby. Use stable IDs for the tab and panel relationships while
preserving the existing click and active-state behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@webapp/src/client.tsx`:
- Around line 1160-1181: The createAutomation function generates IDs using only
Date.now(), allowing same-millisecond collisions. Update the ID generation in
createAutomation to append a unique per-creation value while preserving the
mock- identifier format, so mockAutomations entries remain distinct for React
keys and updateAutomation/deleteAutomation lookups.

In `@webapp/src/components/agents/automation_config_view.tsx`:
- Around line 51-58: Update toUpdate and the AutomationUpdate model to include
the context selector value, and include context in isDirty comparisons so
changes are persisted and trigger the unsaved-changes prompt. Reuse the existing
context state/control value and ensure the automation model carries it through
save and load flows; do not leave the selector as local-only state.

---

Nitpick comments:
In `@webapp/src/components/agents/agent_selector.tsx`:
- Around line 25-40: The MCP/tool metadata derivation is duplicated between
formatAgentMeta and formatPickerMeta. Create a shared helper for distinct
server_origin counting and the all-tools versus pluralized tools label, then
update formatAgentMeta in webapp/src/components/agents/agent_selector.tsx:25-40
and formatPickerMeta in
webapp/src/components/agents/new_automation_agent_menu.tsx:24-46 to use it while
preserving their different separators and channel labels.

In `@webapp/src/components/agents/agents_page.tsx`:
- Around line 42-59: Add ARIA tab semantics around the existing activeTab
controls: apply role="tablist" to TabsBar, role="tab" and aria-selected based on
$active to each PageTabButton, and associate the rendered tab panel with the
selected tab using role="tabpanel" and aria-labelledby. Use stable IDs for the
tab and panel relationships while preserving the existing click and active-state
behavior.

In `@webapp/src/components/agents/automations_list.tsx`:
- Around line 86-95: In the automation action configuration, update the
provider_type value within the run-agent action to use the exported
AIProviderTypeAgent constant from `@/types/automations` instead of the literal
'agent'. Preserve the surrounding action and prompt configuration.
- Around line 137-171: Update the profile-fetching useEffect and its surrounding
state in automations_list.tsx to track creator IDs already attempted, using a
stable attempted-ID set/ref so unresolved IDs are not fetched again after
unrelated automations or profiles changes. Build missingIds by excluding both
cached profiles and attempted IDs, mark IDs as attempted before calling
getProfilesByIds, and preserve the existing cancellation and RECEIVED_PROFILES
dispatch behavior.

In `@webapp/src/components/agents/config_view_shell.tsx`:
- Around line 111-128: Add tab semantics to both tab strips: in
webapp/src/components/agents/config_view_shell.tsx lines 111-128, give
TabsContainer role="tablist" and each TabButton role="tab" with aria-selected
reflecting activeTabId === tab.id; in
webapp/src/components/agents/filter_tabs.tsx lines 25-42, give TabBar
role="tablist" and each ownership TabButton role="tab" with aria-selected
reflecting whether value is "all" or "yours".

In `@webapp/src/components/agents/new_automation_agent_menu.tsx`:
- Around line 214-216: Update the agent display logic surrounding the fallback
return in the new automation agent menu to use the agent’s real description
field instead of custom instructions. If the description field is not yet
available, confirm and implement the intended replacement fallback before
merging, and remove the temporary TODO.
- Around line 156-198: The dropdown in the open state has an invalid listbox
structure and lacks Escape handling. Update the JSX around Dropdown and
OptionsList so the listbox role applies only to the agent options (or implement
the equivalent combobox with an owned listbox), keeping the header and
SearchInput outside that role; add keyboard handling tied to the existing
open/close state so pressing Escape closes the dropdown and preserves the
current focus behavior.

In `@webapp/src/components/agents/row_actions_menu.tsx`:
- Around line 29-42: The open-menu effect in the row actions component only
handles outside mousedown events. Add a keydown listener while open that calls
updateOpen(false) when the Escape key is pressed, and remove that listener
during cleanup alongside the existing mousedown listener.

In `@webapp/src/types/automations.ts`:
- Around line 15-16: Update the automation type containing provider_type to use
the literal union typeof AIProviderTypeAgent | typeof AIProviderTypeService
instead of string. Reuse the exported constants AIProviderTypeAgent and
AIProviderTypeService so only the two supported provider values are accepted.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro

Run ID: ad7e41a7-8e11-41d7-9fe8-86a7b68aac88

📥 Commits

Reviewing files that changed from the base of the PR and between cf496cd and b16302a.

📒 Files selected for processing (18)
  • webapp/src/client.tsx
  • webapp/src/components/agents/agent_config_view.tsx
  • webapp/src/components/agents/agent_row.tsx
  • webapp/src/components/agents/agent_selector.tsx
  • webapp/src/components/agents/agents_list.tsx
  • webapp/src/components/agents/agents_page.tsx
  • webapp/src/components/agents/automation_config_view.tsx
  • webapp/src/components/agents/automation_row.tsx
  • webapp/src/components/agents/automations_list.tsx
  • webapp/src/components/agents/avatar.tsx
  • webapp/src/components/agents/config_view_shell.tsx
  • webapp/src/components/agents/filter_tabs.tsx
  • webapp/src/components/agents/new_automation_agent_menu.tsx
  • webapp/src/components/agents/row_actions_menu.tsx
  • webapp/src/components/agents/trigger_selector.tsx
  • webapp/src/components/select.tsx
  • webapp/src/i18n/en.json
  • webapp/src/types/automations.ts

Comment thread webapp/src/client.tsx
Comment on lines +1160 to +1181
export async function createAutomation(data: AutomationUpdate): Promise<Automation> {
const now = Date.now();
let createdBy = '';
try {
const me = await Client4.getMe();
createdBy = me.id;
} catch {
createdBy = '';
}
const created: Automation = {
id: `mock-${now}`,
name: data.name,
enabled: data.enabled ?? true,
trigger: data.trigger,
actions: data.actions,
created_at: now,
updated_at: now,
created_by: createdBy,
};
mockAutomations = [...mockAutomations, created];
return cloneAutomation(created);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Generated automation id can collide. mock-${now} uses only Date.now(), so two automations created within the same millisecond share an id. Downstream this breaks React key uniqueness in the list and causes updateAutomation/deleteAutomation (which match by id) to hit the wrong record.

Proposed fix
-    const created: Automation = {
-        id: `mock-${now}`,
+    const created: Automation = {
+        id: `mock-${now}-${Math.random().toString(36).slice(2, 8)}`,
📝 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
export async function createAutomation(data: AutomationUpdate): Promise<Automation> {
const now = Date.now();
let createdBy = '';
try {
const me = await Client4.getMe();
createdBy = me.id;
} catch {
createdBy = '';
}
const created: Automation = {
id: `mock-${now}`,
name: data.name,
enabled: data.enabled ?? true,
trigger: data.trigger,
actions: data.actions,
created_at: now,
updated_at: now,
created_by: createdBy,
};
mockAutomations = [...mockAutomations, created];
return cloneAutomation(created);
}
export async function createAutomation(data: AutomationUpdate): Promise<Automation> {
const now = Date.now();
let createdBy = '';
try {
const me = await Client4.getMe();
createdBy = me.id;
} catch {
createdBy = '';
}
const created: Automation = {
id: `mock-${now}-${Math.random().toString(36).slice(2, 8)}`,
name: data.name,
enabled: data.enabled ?? true,
trigger: data.trigger,
actions: data.actions,
created_at: now,
updated_at: now,
created_by: createdBy,
};
mockAutomations = [...mockAutomations, created];
return cloneAutomation(created);
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@webapp/src/client.tsx` around lines 1160 - 1181, The createAutomation
function generates IDs using only Date.now(), allowing same-millisecond
collisions. Update the ID generation in createAutomation to append a unique
per-creation value while preserving the mock- identifier format, so
mockAutomations entries remain distinct for React keys and
updateAutomation/deleteAutomation lookups.

Comment on lines +51 to +58
function toUpdate(automation: Automation): AutomationUpdate {
return {
name: automation.name,
enabled: automation.enabled,
trigger: automation.trigger,
actions: automation.actions,
};
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

rg -n "context" webapp/src/types/automations.ts webapp/src/client.tsx

Repository: mattermost/mattermost-plugin-agents

Length of output: 273


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- automation_config_view.tsx (outline) ---'
ast-grep outline webapp/src/components/agents/automation_config_view.tsx --view expanded || true

echo
echo '--- automations types (outline) ---'
ast-grep outline webapp/src/types/automations.ts --view expanded || true

echo
echo '--- search AutomationUpdate / selectedContext / contextOptions ---'
rg -n "AutomationUpdate|selectedContext|contextOptions|isDirty|toUpdate\\(" webapp/src/components/agents/automation_config_view.tsx webapp/src/types/automations.ts webapp/src -g '!**/*.map'

Repository: mattermost/mattermost-plugin-agents

Length of output: 6959


🏁 Script executed:

#!/bin/bash
set -euo pipefail

sed -n '1,260p' webapp/src/components/agents/automation_config_view.tsx | nl -ba | sed -n '1,260p'
echo '---'
sed -n '1,260p' webapp/src/types/automations.ts | nl -ba | sed -n '1,260p'

Repository: mattermost/mattermost-plugin-agents

Length of output: 213


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- webapp/src/types/automations.ts ---'
sed -n '1,180p' webapp/src/types/automations.ts

echo
echo '--- webapp/src/components/agents/automation_config_view.tsx (relevant ranges) ---'
sed -n '1,260p' webapp/src/components/agents/automation_config_view.tsx
echo '---'
sed -n '260,450p' webapp/src/components/agents/automation_config_view.tsx

echo
echo '--- save path usages ---'
sed -n '1140,1205p' webapp/src/client.tsx

Repository: mattermost/mattermost-plugin-agents

Length of output: 21809


Persist the context selector

context is still local-only state: toUpdate() and AutomationUpdate omit it, and isDirty compares only the automation payload. Changing “Where the automation can read from” can be saved without affecting the stored automation, and it won’t trigger the unsaved-changes prompt when it’s the only edit.

Wire context through the automation model, or remove this control until it’s backed by persisted state.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@webapp/src/components/agents/automation_config_view.tsx` around lines 51 -
58, Update toUpdate and the AutomationUpdate model to include the context
selector value, and include context in isDirty comparisons so changes are
persisted and trigger the unsaved-changes prompt. Reuse the existing context
state/control value and ensure the automation model carries it through save and
load flows; do not leave the selector as local-only state.

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