From a825552835de9d888069842f8b5e3dd737eea36c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pascal=20Andr=C3=A9?= Date: Sun, 7 Jun 2026 17:24:07 +0200 Subject: [PATCH] fix: 090 rescope PR 385 agent selector behavior Keep the PR 385 rescope local to the UI agent selector and session agent helpers. Main sessions now derive selectable options from visible primary agents only, while child sessions preserve the currently active hidden agent so subagent steering remains available. Adds focused node:test coverage for the primary-session filtering and child-session current-hidden-agent behavior. Server config merge and plugin retry code are not included. --- packages/ui/src/components/agent-selector.tsx | 9 +--- packages/ui/src/types/session.test.ts | 45 +++++++++++++++++++ packages/ui/src/types/session.ts | 17 +++++++ 3 files changed, 64 insertions(+), 7 deletions(-) create mode 100644 packages/ui/src/types/session.test.ts 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