Pārlūkot izejas kodu

fix(app): show selector for custom agents (#37198)

Co-authored-by: opencode-agent[bot] <219766164+opencode-agent[bot]@users.noreply.github.com>
Brendan Allan 1 mēnesi atpakaļ
vecāks
revīzija
4a760b5743

+ 29 - 0
packages/app/src/context/local-agent.test.ts

@@ -0,0 +1,29 @@
+import { describe, expect, test } from "bun:test"
+import { hasCustomAgent, resolveAgent } from "./local-agent"
+
+describe("hasCustomAgent", () => {
+  test("detects explicitly custom agents", () => {
+    expect(hasCustomAgent([{ native: true }, { native: false }])).toBe(true)
+  })
+
+  test("ignores built-in and unclassified agents", () => {
+    expect(hasCustomAgent([{ native: true }, {}])).toBe(false)
+  })
+})
+
+describe("resolveAgent", () => {
+  const agents = [{ name: "plan" }, { name: "build" }, { name: "custom" }]
+
+  test("uses the requested available agent", () => {
+    expect(resolveAgent(agents, "custom")?.name).toBe("custom")
+  })
+
+  test("defaults to build", () => {
+    expect(resolveAgent(agents)?.name).toBe("build")
+    expect(resolveAgent(agents, "missing")?.name).toBe("build")
+  })
+
+  test("uses the first agent when build is unavailable", () => {
+    expect(resolveAgent([{ name: "custom" }], "missing")?.name).toBe("custom")
+  })
+})

+ 7 - 0
packages/app/src/context/local-agent.ts

@@ -0,0 +1,7 @@
+export function hasCustomAgent(items: Array<{ native?: boolean }>) {
+  return items.some((item) => item.native === false)
+}
+
+export function resolveAgent<T extends { name: string }>(items: T[], name?: string) {
+  return items.find((item) => item.name === name) ?? items.find((item) => item.name === "build") ?? items[0]
+}

+ 7 - 4
packages/app/src/context/local.tsx

@@ -4,8 +4,10 @@ import { useParams } from "@solidjs/router"
 import { batch, createEffect, createMemo, startTransition } from "solid-js"
 import { createStore } from "solid-js/store"
 import { useModels } from "@/context/models"
+import { useSettings } from "@/context/settings"
 import { useProviders } from "@/hooks/use-providers"
 import { Persist, persisted } from "@/utils/persist"
+import { hasCustomAgent, resolveAgent } from "./local-agent"
 import { cycleModelVariant, getConfiguredAgentVariant, resolveModelVariant } from "./model-variant"
 import { useSDK } from "./sdk"
 import { useSync } from "./sync"
@@ -62,9 +64,11 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
     const serverSDK = useServerSDK()
     const providers = useProviders(() => sdk().directory)
     const models = useModels()
+    const settings = useSettings()
 
     const id = createMemo(() => params.id || undefined)
     const list = createMemo(() => sync().data.agent.filter((item) => item.mode !== "subagent" && !item.hidden))
+    const agentsVisible = createMemo(() => settings.visibility.customAgents() || hasCustomAgent(list()))
     const connected = createMemo(() => new Set(providers.connected().map((item) => item.id)))
 
     const [saved, setSaved, , savedReady] = persisted(
@@ -107,9 +111,7 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
     }
 
     const pickAgent = (name: string | undefined) => {
-      const items = list()
-      if (items.length === 0) return
-      return items.find((item) => item.name === name) ?? items[0]
+      return resolveAgent(list(), name)
     }
 
     createEffect(() => {
@@ -180,8 +182,9 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
 
     const agent = {
       list,
+      visible: agentsVisible,
       current() {
-        return pickAgent(scope()?.agent ?? store.current)
+        return pickAgent(agentsVisible() ? (scope()?.agent ?? store.current) : "build")
       },
       set(name: string | undefined) {
         const item = pickAgent(name)

+ 1 - 1
packages/app/src/pages/session/composer/session-composer-controls.ts

@@ -41,7 +41,7 @@ export function createPromptInputController(input: {
       options: local.agent.list().map((agent) => agent.name),
       current: local.agent.current()?.name ?? "",
       loading: agentsQuery.isLoading,
-      visible: settings.visibility.customAgents(),
+      visible: local.agent.visible(),
       select: local.agent.set,
     },
     model: {

+ 2 - 4
packages/app/src/pages/session/use-composer-commands.tsx

@@ -1,7 +1,6 @@
 import { useCommand, type CommandOption } from "@/context/command"
 import { useLanguage } from "@/context/language"
 import { useLocal, type ModelSelection } from "@/context/local"
-import { useSettings } from "@/context/settings"
 import { useDialog } from "@opencode-ai/ui/context/dialog"
 import { getCursorPosition, setCursorPosition } from "@/components/prompt-input/editor-dom"
 import { useSessionLayout } from "./session-layout"
@@ -19,7 +18,6 @@ export const useComposerCommands = (input: { model?: ModelSelection } = {}) => {
   const dialog = useDialog()
   const language = useLanguage()
   const local = useLocal()
-  const settings = useSettings()
   const { sessionKey } = useSessionLayout()
   const sessionOwnership = createSessionOwnership(sessionKey)
   const model = input.model ?? local.model
@@ -70,7 +68,7 @@ export const useComposerCommands = (input: { model?: ModelSelection } = {}) => {
       description: language.t("command.agent.cycle.description"),
       keybind: "mod+.",
       slash: "agent",
-      disabled: !settings.visibility.customAgents(),
+      disabled: !local.agent.visible(),
       onSelect: () => local.agent.move(1),
     }),
     agentCommand({
@@ -78,7 +76,7 @@ export const useComposerCommands = (input: { model?: ModelSelection } = {}) => {
       title: language.t("command.agent.cycle.reverse"),
       description: language.t("command.agent.cycle.reverse.description"),
       keybind: "shift+mod+.",
-      disabled: !settings.visibility.customAgents(),
+      disabled: !local.agent.visible(),
       onSelect: () => local.agent.move(-1),
     }),
   ])