fix: filter hidden internal agents from UI and updated sdk version

This commit is contained in:
Bohdan Triapitsyn
2025-12-18 19:02:42 +02:00
parent b878c620b9
commit e6b07a0563
12 changed files with 80 additions and 20 deletions
@@ -34,10 +34,11 @@ export const AgentMentionAutocomplete = React.forwardRef<AgentMentionAutocomplet
const containerRef = React.useRef<HTMLDivElement | null>(null);
const [selectedIndex, setSelectedIndex] = React.useState(0);
const [agents, setAgents] = React.useState<AgentInfo[]>([]);
const { agents: allAgents } = useConfigStore();
const { getVisibleAgents } = useConfigStore();
React.useEffect(() => {
const filtered = allAgents
const visibleAgents = getVisibleAgents();
const filtered = visibleAgents
.filter((agent) => isMentionable(agent.mode))
.map((agent) => ({
name: agent.name,
@@ -54,7 +55,7 @@ export const AgentMentionAutocomplete = React.forwardRef<AgentMentionAutocomplet
setAgents(matches);
setSelectedIndex(0);
}, [allAgents, searchQuery]);
}, [getVisibleAgents, searchQuery]);
React.useEffect(() => {
const handlePointerDown = (event: MouseEvent | TouchEvent) => {
@@ -72,7 +72,8 @@ export const ChatInput: React.FC<ChatInputProps> = ({ onOpenSettings, scrollToBo
const clearAttachedFiles = useSessionStore((state) => state.clearAttachedFiles);
const saveSessionAgentSelection = useSessionStore((state) => state.saveSessionAgentSelection);
const { currentProviderId, currentModelId, currentAgentName, agents, setAgent } = useConfigStore();
const { currentProviderId, currentModelId, currentAgentName, setAgent, getVisibleAgents } = useConfigStore();
const agents = getVisibleAgents();
const { isMobile } = useUIStore();
const { working } = useAssistantStatus();
const [showAbortStatus, setShowAbortStatus] = React.useState(false);
@@ -209,7 +209,6 @@ interface ModelControlsProps {
export const ModelControls: React.FC<ModelControlsProps> = ({ className }) => {
const {
providers,
agents,
currentProviderId,
currentModelId,
currentAgentName,
@@ -219,8 +218,12 @@ export const ModelControls: React.FC<ModelControlsProps> = ({ className }) => {
getCurrentProvider,
getModelMetadata,
getCurrentAgent,
getVisibleAgents,
} = useConfigStore();
// Use visible agents (excludes hidden internal agents)
const agents = getVisibleAgents();
const {
currentSessionId,
messages,
@@ -19,7 +19,7 @@ import {
DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu';
import { RiAddLine, RiAiAgentFill, RiAiAgentLine, RiDeleteBinLine, RiFileCopyLine, RiMore2Line, RiRobot2Line, RiRobotLine } from '@remixicon/react';
import { useAgentsStore } from '@/stores/useAgentsStore';
import { useAgentsStore, isAgentBuiltIn, isAgentHidden } from '@/stores/useAgentsStore';
import { useUIStore } from '@/stores/useUIStore';
import { useDeviceInfo } from '@/lib/device';
import { cn } from '@/lib/utils';
@@ -66,7 +66,7 @@ export const AgentsSidebar: React.FC = () => {
};
const handleDeleteAgent = async (agent: Agent) => {
if (agent.builtIn) {
if (isAgentBuiltIn(agent)) {
toast.error('Built-in agents cannot be deleted');
return;
}
@@ -112,8 +112,10 @@ export const AgentsSidebar: React.FC = () => {
}
};
const builtInAgents = agents.filter((agent) => agent.builtIn);
const customAgents = agents.filter((agent) => !agent.builtIn);
// Filter out hidden agents (internal agents like title, compaction, summary)
const visibleAgents = agents.filter((agent) => !isAgentHidden(agent));
const builtInAgents = visibleAgents.filter(isAgentBuiltIn);
const customAgents = visibleAgents.filter((agent) => !isAgentBuiltIn(agent));
return (
<div className="flex h-full flex-col bg-sidebar">
@@ -122,7 +124,7 @@ export const AgentsSidebar: React.FC = () => {
<div className="flex items-center justify-between gap-2">
<h2 className="typography-ui-label font-semibold text-foreground">Agents</h2>
<div className="flex items-center gap-1">
<span className="typography-meta text-muted-foreground">{agents.length}</span>
<span className="typography-meta text-muted-foreground">{visibleAgents.length}</span>
<DialogTrigger asChild>
<Button type="button" variant="ghost" size="icon" className="h-7 w-7 text-muted-foreground">
<RiAddLine className="size-4" />
@@ -133,7 +135,7 @@ export const AgentsSidebar: React.FC = () => {
</div>
<ScrollableOverlay outerClassName="flex-1 min-h-0" className="space-y-1 px-3 py-2 overflow-x-hidden">
{agents.length === 0 ? (
{visibleAgents.length === 0 ? (
<div className="py-12 px-4 text-center text-muted-foreground">
<RiRobot2Line className="mx-auto mb-3 h-10 w-10 opacity-50" />
<p className="typography-ui-label font-medium">No agents configured</p>
@@ -297,7 +299,7 @@ const AgentListItem: React.FC<AgentListItemProps> = ({
Duplicate
</DropdownMenuItem>
{!agent.builtIn && onDelete && (
{!isAgentBuiltIn(agent) && onDelete && (
<DropdownMenuItem
onClick={(e) => {
e.stopPropagation();
@@ -23,7 +23,8 @@ export const AgentSelector: React.FC<AgentSelectorProps> = ({
onChange,
className
}) => {
const { agents, loadAgents } = useAgentsStore();
const { loadAgents, getVisibleAgents } = useAgentsStore();
const agents = getVisibleAgents();
const isMobile = useUIStore(state => state.isMobile);
const { isMobile: deviceIsMobile } = useDeviceInfo();
const isActuallyMobile = isMobile || deviceIsMobile;
@@ -398,6 +398,10 @@ export const useChatScrollManager = ({
spacerHeightRef.current = 0;
setSpacerHeight(0);
setPendingAnchorId(null);
setShowScrollButton(false);
userScrollOverrideRef.current = false;
}
// eslint-disable-next-line react-hooks/exhaustive-deps -- only run on session change, not message changes
@@ -480,6 +484,21 @@ export const useChatScrollManager = ({
};
}, [refreshSpacer, updateScrollButtonVisibility]);
React.useEffect(() => {
if (typeof window === 'undefined') {
updateScrollButtonVisibility();
return;
}
const rafId = window.requestAnimationFrame(() => {
updateScrollButtonVisibility();
});
return () => {
window.cancelAnimationFrame(rafId);
};
}, [currentSessionId, sessionMessages.length, updateScrollButtonVisibility]);
React.useEffect(() => {
if (anchorId) {
refreshSpacer();
+22
View File
@@ -30,6 +30,21 @@ export interface AgentConfig {
disable?: boolean;
}
// Extended Agent type for API properties not in SDK types
export type AgentWithExtras = Agent & { native?: boolean; hidden?: boolean };
// Helper to check if agent is built-in (handles both SDK 'builtIn' and API 'native')
export const isAgentBuiltIn = (agent: Agent): boolean =>
agent.builtIn || (agent as AgentWithExtras).native === true;
// Helper to check if agent is hidden (internal agents like title, compaction, summary)
export const isAgentHidden = (agent: Agent): boolean =>
(agent as AgentWithExtras).hidden === true;
// Helper to filter only visible (non-hidden) agents
export const filterVisibleAgents = (agents: Agent[]): Agent[] =>
agents.filter((agent) => !isAgentHidden(agent));
const CONFIG_EVENT_SOURCE = "useAgentsStore";
const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
const MAX_HEALTH_WAIT_MS = 20000;
@@ -51,6 +66,8 @@ interface AgentsStore {
updateAgent: (name: string, config: Partial<AgentConfig>) => Promise<boolean>;
deleteAgent: (name: string) => Promise<boolean>;
getAgentByName: (name: string) => Agent | undefined;
// Returns only visible agents (excludes hidden internal agents)
getVisibleAgents: () => Agent[];
}
declare global {
@@ -264,6 +281,11 @@ export const useAgentsStore = create<AgentsStore>()(
const { agents } = get();
return agents.find((a) => a.name === name);
},
getVisibleAgents: () => {
const { agents } = get();
return filterVisibleAgents(agents);
},
}),
{
name: "agents-store",
+7
View File
@@ -7,6 +7,7 @@ import { scopeMatches, subscribeToConfigChanges } from "@/lib/configSync";
import type { ModelMetadata } from "@/types";
import { getSafeStorage } from "./utils/safeStorage";
import type { SessionStore } from "./types/sessionTypes";
import { filterVisibleAgents } from "./useAgentsStore";
const MODELS_DEV_API_URL = "https://models.dev/api.json";
const MODELS_DEV_PROXY_URL = "/api/openchamber/models-metadata";
@@ -230,6 +231,8 @@ interface ConfigStore {
getCurrentModel: () => ProviderModel | undefined;
getCurrentAgent: () => Agent | undefined;
getModelMetadata: (providerId: string, modelId: string) => ModelMetadata | undefined;
// Returns only visible agents (excludes hidden internal agents like title, compaction, summary)
getVisibleAgents: () => Agent[];
}
declare global {
@@ -522,6 +525,10 @@ export const useConfigStore = create<ConfigStore>()(
const { modelsMetadata } = get();
return modelsMetadata.get(key);
},
getVisibleAgents: () => {
const { agents } = get();
return filterVisibleAgents(agents);
},
}),
{
name: "config-store",