[MM-67118] Add Agents to @ mention autocomplete in channel (#34881)
Some checks are pending
API / build (push) Waiting to run
Server CI / Compute Go Version (push) Waiting to run
Server CI / Check mocks (push) Blocked by required conditions
Server CI / Check go mod tidy (push) Blocked by required conditions
Server CI / check-style (push) Blocked by required conditions
Server CI / Check serialization methods for hot structs (push) Blocked by required conditions
Server CI / Vet API (push) Blocked by required conditions
Server CI / Check migration files (push) Blocked by required conditions
Server CI / Generate email templates (push) Blocked by required conditions
Server CI / Check store layers (push) Blocked by required conditions
Server CI / Check mmctl docs (push) Blocked by required conditions
Server CI / Postgres with binary parameters (push) Blocked by required conditions
Server CI / Postgres (push) Blocked by required conditions
Server CI / Postgres (FIPS) (push) Blocked by required conditions
Server CI / Generate Test Coverage (push) Blocked by required conditions
Server CI / Run mmctl tests (push) Blocked by required conditions
Server CI / Run mmctl tests (FIPS) (push) Blocked by required conditions
Server CI / Build mattermost server app (push) Blocked by required conditions
Web App CI / check-lint (push) Waiting to run
Web App CI / check-i18n (push) Blocked by required conditions
Web App CI / check-types (push) Blocked by required conditions
Web App CI / test (platform) (push) Blocked by required conditions
Web App CI / test (mattermost-redux) (push) Blocked by required conditions
Web App CI / test (channels shard 1/4) (push) Blocked by required conditions
Web App CI / test (channels shard 2/4) (push) Blocked by required conditions
Web App CI / test (channels shard 3/4) (push) Blocked by required conditions
Web App CI / test (channels shard 4/4) (push) Blocked by required conditions
Web App CI / upload-coverage (push) Blocked by required conditions
Web App CI / build (push) Blocked by required conditions

* Add support for autocomplete of plugin-created bots

* stashing

* Add support for including agents in the autocomplete list for @ mentions

* Change server response to be users rather than interface

* fix
This commit is contained in:
Nick Misasi 2026-01-19 16:10:57 -05:00 committed by GitHub
parent c62d103d76
commit b2f93dec1a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 64 additions and 0 deletions

View file

@ -1324,6 +1324,12 @@ func autocompleteUsers(c *Context, w http.ResponseWriter, r *http.Request) {
autocomplete.Users = result
}
// Fetch agent users for autocomplete
agentUsers, appErr := c.App.GetUsersForAgents(c.AppContext, c.AppContext.Session().UserId)
if appErr == nil && agentUsers != nil {
autocomplete.Agents = agentUsers
}
if err := json.NewEncoder(w).Encode(autocomplete); err != nil {
c.Logger.Warn("Error while writing response", mlog.Err(err))
}

View file

@ -101,6 +101,35 @@ func (a *App) GetAgents(rctx request.CTX, userID string) ([]agentclient.BridgeAg
return agents, nil
}
// GetUsersForAgents retrieves the User objects for all available agents
func (a *App) GetUsersForAgents(rctx request.CTX, userID string) ([]*model.User, *model.AppError) {
agents, appErr := a.GetAgents(rctx, userID)
if appErr != nil {
return nil, appErr
}
if len(agents) == 0 {
return []*model.User{}, nil
}
users := make([]*model.User, 0, len(agents))
for _, agent := range agents {
// Agents have a username field that corresponds to the bot user's username
user, err := a.Srv().Store().User().GetByUsername(agent.Username)
if err != nil {
rctx.Logger().Warn("Failed to get user for agent",
mlog.Err(err),
mlog.String("agent_id", agent.ID),
mlog.String("username", agent.Username),
)
continue
}
users = append(users, user)
}
return users, nil
}
// GetLLMServices retrieves all available LLM services from the bridge API
func (a *App) GetLLMServices(rctx request.CTX, userID string) ([]agentclient.BridgeServiceInfo, *model.AppError) {
// Check if the AI plugin is active and supports the bridge API (v1.5.0+)

View file

@ -15,4 +15,5 @@ type UserAutocompleteInTeam struct {
type UserAutocomplete struct {
Users []*User `json:"users"`
OutOfChannel []*User `json:"out_of_channel,omitempty"`
Agents []*User `json:"agents,omitempty"`
}

View file

@ -1,8 +1,10 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React from 'react';
import {defineMessage} from 'react-intl';
import {CreationOutlineIcon} from '@mattermost/compass-icons/components';
import type {Group} from '@mattermost/types/groups';
import type {UserProfile} from '@mattermost/types/users';
@ -306,6 +308,17 @@ export default class AtMentionProvider extends Provider {
// Combine the local and remote members, sorting to mix the results together.
const localAndRemoteMembers = localMembers.concat(remoteMembers).sort(orderUsers);
// Get agents - these are already User objects from the backend
// Only show agents if bridge is enabled (indicated by presence of agents data)
let agents: CreatedProfile[] = [];
if (this.data && this.data.agents && Array.isArray(this.data.agents) && this.data.agents.length > 0) {
const agentUsers = this.data.agents as UserProfileWithLastViewAt[];
agents = agentUsers.
filter((user: UserProfileWithLastViewAt) => this.filterProfile(user)).
map((user: UserProfileWithLastViewAt) => this.createFromProfile(user)).
sort(orderUsers);
}
// handle groups
const localGroups = this.localGroups();
@ -345,6 +358,9 @@ export default class AtMentionProvider extends Provider {
if (priorityProfiles.length > 0 || localAndRemoteMembers.length > 0) {
items.push(membersGroup([...priorityProfiles, ...localAndRemoteMembers]));
}
if (agents.length > 0) {
items.push(agentsGroup(agents));
}
if (localAndRemoteGroups.length > 0) {
items.push(groupsGroup(localAndRemoteGroups));
}
@ -453,6 +469,17 @@ export function membersGroup(items: CreatedProfile[]) {
};
}
export function agentsGroup(items: CreatedProfile[]) {
return {
key: 'agents',
label: defineMessage({id: 'suggestion.mention.agents', defaultMessage: 'Agents'}),
icon: <CreationOutlineIcon size={16}/>,
items,
terms: items.map((profile) => '@' + profile.username),
component: AtMentionSuggestion,
};
}
export function groupsGroup(items: Group[]) {
return {
key: 'groups',

View file

@ -5990,6 +5990,7 @@
"suggestion.emoji": "Emoji",
"suggestion.group_channel": "Group channel",
"suggestion.group.members": "{member_count} {member_count, plural, one {member} other {members}}",
"suggestion.mention.agents": "Agents",
"suggestion.mention.all": "Notifies everyone in this channel",
"suggestion.mention.channel": "Notifies everyone in this channel",
"suggestion.mention.channels": "My Channels",