refactor(ui): unify primary agent filtering for mentions and UI (#339)

This commit is contained in:
Bohdan Triapitsyn
2026-02-06 15:27:16 +02:00
committed by GitHub
parent 0503f51357
commit b166d831ce
3 changed files with 21 additions and 25 deletions
@@ -18,6 +18,11 @@ export interface AgentMentionAutocompleteHandle {
type AutocompleteTab = 'commands' | 'agents' | 'files';
const isMentionableAgentMode = (mode?: string | null): boolean => {
if (!mode) return false;
return mode !== 'primary';
};
interface AgentMentionAutocompleteProps {
searchQuery: string;
onAgentSelect: (agentName: string) => void;
@@ -27,14 +32,7 @@ interface AgentMentionAutocompleteProps {
onTabSelect?: (tab: AutocompleteTab) => void;
}
const isMentionable = (mode?: string | null): boolean => {
if (!mode) {
return false;
}
return mode !== 'primary';
};
export const AgentMentionAutocomplete = React.forwardRef<AgentMentionAutocompleteHandle, AgentMentionAutocompleteProps>(({
export const AgentMentionAutocomplete = React.forwardRef<AgentMentionAutocompleteHandle, AgentMentionAutocompleteProps>(({
searchQuery,
onAgentSelect,
onClose,
@@ -59,7 +57,7 @@ export const AgentMentionAutocomplete = React.forwardRef<AgentMentionAutocomplet
React.useEffect(() => {
const visibleAgents = getVisibleAgents();
const filtered = visibleAgents
.filter((agent) => isMentionable(agent.mode))
.filter((agent) => isMentionableAgentMode(agent.mode))
.map((agent) => {
const metadata = agentsWithMetadata.find(a => a.name === agent.name) as (AgentWithExtras & { scope?: string }) | undefined;
return {
@@ -54,8 +54,6 @@ interface ChatInputProps {
scrollToBottom?: (options?: { instant?: boolean; force?: boolean; clearAnchor?: boolean }) => void;
}
const isPrimaryMode = (mode?: string) => mode === 'primary' || mode === 'all' || mode === undefined || mode === null;
const CHAT_INPUT_DRAFT_KEY = 'openchamber_chat_input_draft';
// Helper to safely read from localStorage
@@ -118,6 +116,7 @@ export const ChatInput: React.FC<ChatInputProps> = ({ onOpenSettings, scrollToBo
const { currentProviderId, currentModelId, currentVariant, currentAgentName, setAgent, getVisibleAgents } = useConfigStore();
const agents = getVisibleAgents();
const primaryAgents = React.useMemo(() => agents.filter((agent) => agent.mode === 'primary'), [agents]);
const { isMobile, inputBarOffset, isKeyboardOpen, setTimelineDialogOpen, cornerRadius, persistChatDraft } = useUIStore();
const { working } = useAssistantStatus();
const { currentTheme } = useThemeSystem();
@@ -799,7 +798,6 @@ export const ChatInput: React.FC<ChatInputProps> = ({ onOpenSettings, scrollToBo
}, [abortCurrentOperation, clearAbortPrompt, startAbortIndicator]);
const handleCycleAgent = React.useCallback(() => {
const primaryAgents = agents.filter(agent => isPrimaryMode(agent.mode));
if (primaryAgents.length <= 1) return;
const currentIndex = primaryAgents.findIndex(agent => agent.name === currentAgentName);
@@ -811,7 +809,7 @@ export const ChatInput: React.FC<ChatInputProps> = ({ onOpenSettings, scrollToBo
if (currentSessionId) {
saveSessionAgentSelection(currentSessionId, nextAgent.name);
}
}, [agents, currentAgentName, currentSessionId, setAgent, saveSessionAgentSelection]);
}, [primaryAgents, currentAgentName, currentSessionId, setAgent, saveSessionAgentSelection]);
const adjustTextareaHeight = React.useCallback(() => {
const textarea = textareaRef.current;
@@ -57,8 +57,6 @@ type IconComponent = ComponentType<any>;
type ProviderModel = Record<string, unknown> & { id?: string; name?: string };
const isPrimaryMode = (mode?: string) => mode === 'primary' || mode === 'all' || mode === undefined || mode === null;
type PermissionAction = 'allow' | 'ask' | 'deny';
type PermissionRule = { permission: string; pattern: string; action: PermissionAction };
@@ -288,6 +286,7 @@ export const ModelControls: React.FC<ModelControlsProps> = ({
// Use visible agents (excludes hidden internal agents)
const agents = getVisibleAgents();
const primaryAgents = React.useMemo(() => agents.filter((agent) => agent.mode === 'primary'), [agents]);
const {
currentSessionId,
@@ -432,8 +431,12 @@ export const ModelControls: React.FC<ModelControlsProps> = ({
setModelSelectedIndex(0);
}, [desktopModelQuery]);
const selectableDesktopAgents = React.useMemo(() => {
return agents.filter((agent) => agent.mode !== 'subagent');
}, [agents]);
const sortedAndFilteredAgents = React.useMemo(() => {
const sorted = [...agents].sort((a, b) => a.name.localeCompare(b.name));
const sorted = [...selectableDesktopAgents].sort((a, b) => a.name.localeCompare(b.name));
if (!agentSearchQuery.trim()) {
return sorted;
}
@@ -441,17 +444,17 @@ export const ModelControls: React.FC<ModelControlsProps> = ({
fuzzyMatch(agentSearchQuery, agent.name) ||
(agent.description && fuzzyMatch(agentSearchQuery, agent.description))
);
}, [agents, agentSearchQuery]);
}, [selectableDesktopAgents, agentSearchQuery]);
const defaultAgentName = React.useMemo(() => {
if (settingsDefaultAgent) {
const found = agents.find(a => a.name === settingsDefaultAgent);
const found = selectableDesktopAgents.find(a => a.name === settingsDefaultAgent);
if (found) return found.name;
}
const buildAgent = agents.find(a => a.name === 'build');
const buildAgent = selectableDesktopAgents.find(a => a.name === 'build');
if (buildAgent) return buildAgent.name;
return agents[0]?.name;
}, [settingsDefaultAgent, agents]);
return selectableDesktopAgents[0]?.name;
}, [settingsDefaultAgent, selectableDesktopAgents]);
const currentAgent = React.useMemo(() => {
if (uiAgentName) {
@@ -696,7 +699,6 @@ export const ModelControls: React.FC<ModelControlsProps> = ({
return;
}
const primaryAgents = agents.filter(agent => isPrimaryMode(agent.mode));
const fallbackAgent = agents.find(agent => agent.name === 'build') || primaryAgents[0] || agents[0];
if (!fallbackAgent) {
return;
@@ -821,6 +823,7 @@ export const ModelControls: React.FC<ModelControlsProps> = ({
currentSessionId,
currentSessionMessageCount,
agents,
primaryAgents,
currentAgentName,
getAgentModelForSession,
setAgent,
@@ -1107,7 +1110,6 @@ export const ModelControls: React.FC<ModelControlsProps> = ({
const getAgentDisplayName = () => {
if (!uiAgentName) {
const primaryAgents = agents.filter(agent => isPrimaryMode(agent.mode));
const buildAgent = primaryAgents.find(agent => agent.name === 'build');
const defaultAgent = buildAgent || primaryAgents[0];
return defaultAgent ? capitalizeAgentName(defaultAgent.name) : 'Select Agent';
@@ -1724,8 +1726,6 @@ export const ModelControls: React.FC<ModelControlsProps> = ({
const renderMobileAgentPanel = () => {
if (!isCompact) return null;
const primaryAgents = agents.filter(agent => isPrimaryMode(agent.mode));
return (
<MobileOverlayPanel
open={activeMobilePanel === 'agent'}