diff --git a/packages/ui/src/components/agent-selector.tsx b/packages/ui/src/components/agent-selector.tsx index b3ef06b35..be3616d35 100644 --- a/packages/ui/src/components/agent-selector.tsx +++ b/packages/ui/src/components/agent-selector.tsx @@ -2,7 +2,7 @@ import { Select } from "@kobalte/core/select" import { Show, createEffect, createMemo } from "solid-js" import { agents, fetchAgents, sessions } from "../stores/sessions" import { ChevronDown } from "lucide-solid" -import { isSelectablePrimaryAgent, type Agent } from "../types/session" +import { getSelectableAgentsForSession, type Agent } from "../types/session" import { useI18n } from "../lib/i18n" import { getLogger } from "../lib/logger" const log = getLogger("session") @@ -29,12 +29,7 @@ export default function AgentSelector(props: AgentSelectorProps) { }) const availableAgents = createMemo(() => { - const allAgents = instanceAgents() - if (isChildSession()) { - return allAgents.filter((agent) => !agent.hidden) - } - - return allAgents.filter(isSelectablePrimaryAgent) + return getSelectableAgentsForSession(instanceAgents(), props.currentAgent, isChildSession()) }) createEffect(() => { diff --git a/packages/ui/src/types/session.test.ts b/packages/ui/src/types/session.test.ts new file mode 100644 index 000000000..0f924c974 --- /dev/null +++ b/packages/ui/src/types/session.test.ts @@ -0,0 +1,45 @@ +import assert from "node:assert/strict" +import { describe, it } from "node:test" + +import { getSelectableAgentsForSession, isSelectablePrimaryAgent, type Agent } from "./session.ts" + +const visiblePrimary: Agent = { name: "plan", description: "", mode: "primary" } +const visibleSubagent: Agent = { name: "review", description: "", mode: "subagent" } +const hiddenPrimary: Agent = { name: "build", description: "", mode: "primary", hidden: true } +const hiddenSubagent: Agent = { name: "debug", description: "", mode: "subagent", hidden: true } + +describe("agent selectability", () => { + it("matches primary-session selector rules", () => { + assert.equal(isSelectablePrimaryAgent(visiblePrimary), true) + assert.equal(isSelectablePrimaryAgent(visibleSubagent), false) + assert.equal(isSelectablePrimaryAgent(hiddenPrimary), false) + assert.equal(isSelectablePrimaryAgent(hiddenSubagent), false) + }) + + it("excludes hidden and subagent entries from main-session selectors", () => { + const agents = [hiddenPrimary, visibleSubagent, visiblePrimary] + + assert.deepEqual( + getSelectableAgentsForSession(agents, "build", false).map((agent) => agent.name), + ["plan"], + ) + }) + + it("preserves a child session's current hidden agent for steering", () => { + const agents = [hiddenPrimary, visibleSubagent, visiblePrimary] + + assert.deepEqual( + getSelectableAgentsForSession(agents, "build", true).map((agent) => agent.name), + ["review", "plan", "build"], + ) + }) + + it("does not add unrelated hidden agents to child-session selectors", () => { + const agents = [hiddenPrimary, visibleSubagent, visiblePrimary] + + assert.deepEqual( + getSelectableAgentsForSession(agents, "review", true).map((agent) => agent.name), + ["review", "plan"], + ) + }) +}) diff --git a/packages/ui/src/types/session.ts b/packages/ui/src/types/session.ts index 1d51be050..067ec63e9 100644 --- a/packages/ui/src/types/session.ts +++ b/packages/ui/src/types/session.ts @@ -120,6 +120,23 @@ export function isSelectablePrimaryAgent(agent: Agent): boolean { return !agent.hidden && agent.mode !== "subagent" } +export function getSelectableAgentsForSession( + agentList: Agent[], + currentAgentName: string, + isChildSession: boolean, +): Agent[] { + if (!isChildSession) { + return agentList.filter(isSelectablePrimaryAgent) + } + + const visibleAgents = agentList.filter((agent) => !agent.hidden) + const currentHiddenAgent = agentList.find((agent) => agent.hidden && agent.name === currentAgentName) + + return currentHiddenAgent && !visibleAgents.some((agent) => agent.name === currentHiddenAgent.name) + ? [...visibleAgents, currentHiddenAgent] + : visibleAgents +} + // Our client-specific Provider interface (simplified version of SDK Provider) export interface Provider { id: string