fix(agent-selector): read agents from active-directory config store

AgentSelector used useAgentsStore which is only warm after a chat session
loads, so the todo send dialog opened from the project context panel showed
an empty agent list. Prefer useConfigStore.agents (the same source ModelControls
uses in chat) with useAgentsStore as fallback, and load both on mount when
neither is populated.

Also set the primary-mode filter on the scheduled task editor agent selector
to keep its options consistent with the other flows.
This commit is contained in:
Bohdan Triapitsyn
2026-04-18 13:47:24 +03:00
parent 8d43678fea
commit 01b4f11e56
2 changed files with 24 additions and 6 deletions
@@ -1,11 +1,13 @@
import React from 'react';
import type { Agent } from '@opencode-ai/sdk/v2';
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu';
import { useAgentsStore } from '@/stores/useAgentsStore';
import { useAgentsStore, filterVisibleAgents } from '@/stores/useAgentsStore';
import { useConfigStore } from '@/stores/useConfigStore';
import { useUIStore } from '@/stores/useUIStore';
import { useDeviceInfo } from '@/lib/device';
import { RiArrowDownSLine, RiRobot2Line } from '@remixicon/react';
@@ -16,15 +18,27 @@ interface AgentSelectorProps {
agentName: string;
onChange: (agentName: string) => void;
className?: string;
filter?: (agent: Agent) => boolean;
}
export const AgentSelector: React.FC<AgentSelectorProps> = ({
agentName,
onChange,
className
className,
filter,
}) => {
const { loadAgents, getVisibleAgents } = useAgentsStore();
const agents = getVisibleAgents();
const configAgents = useConfigStore((state) => state.agents);
const agentsStoreAgents = useAgentsStore((state) => state.agents);
const loadAgentsStore = useAgentsStore((state) => state.loadAgents);
const loadConfigAgents = useConfigStore((state) => state.loadAgents);
const rawAgents = React.useMemo(() => {
if (Array.isArray(configAgents) && configAgents.length > 0) return configAgents;
return Array.isArray(agentsStoreAgents) ? agentsStoreAgents : [];
}, [configAgents, agentsStoreAgents]);
const agents = React.useMemo(() => {
const visible = filterVisibleAgents(rawAgents);
return filter ? visible.filter(filter) : visible;
}, [rawAgents, filter]);
const isMobile = useUIStore(state => state.isMobile);
const { isMobile: deviceIsMobile } = useDeviceInfo();
const isActuallyMobile = isMobile || deviceIsMobile;
@@ -32,8 +46,10 @@ export const AgentSelector: React.FC<AgentSelectorProps> = ({
const [isMobilePanelOpen, setIsMobilePanelOpen] = React.useState(false);
React.useEffect(() => {
loadAgents();
}, [loadAgents]);
if (rawAgents.length > 0) return;
void loadConfigAgents();
void loadAgentsStore();
}, [rawAgents.length, loadConfigAgents, loadAgentsStore]);
const closeMobilePanel = () => setIsMobilePanelOpen(false);
@@ -12,6 +12,7 @@ import { toast } from '@/components/ui';
import { RiAddLine, RiCloseLine, RiCalendarLine, RiArrowLeftSLine, RiArrowRightSLine, RiArrowDownSLine } from '@remixicon/react';
import { ModelSelector } from '@/components/sections/agents/ModelSelector';
import { AgentSelector } from '@/components/sections/commands/AgentSelector';
import { isPrimaryMode } from '@/components/chat/mobileControlsUtils';
import { CommandAutocomplete, type CommandAutocompleteHandle } from '@/components/chat/CommandAutocomplete';
import { FileMentionAutocomplete, type FileMentionHandle } from '@/components/chat/FileMentionAutocomplete';
import { useConfigStore } from '@/stores/useConfigStore';
@@ -1308,6 +1309,7 @@ export function ScheduledTaskEditorDialog(props: {
<FieldLabel>Agent</FieldLabel>
<AgentSelector
agentName={draft.execution.agent}
filter={(agent) => isPrimaryMode(agent.mode)}
onChange={(agent) => setDraft((prev) => ({
...prev,
execution: {