Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export function DialogAgent() {
return (
<DialogSelect
title="Select agent"
current={local.agent.current().name}
current={local.agent.current()?.name ?? ""}
options={options()}
onSelect={(option) => {
local.agent.set(option.value)
Expand Down
17 changes: 11 additions & 6 deletions packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -530,11 +530,13 @@ export function Prompt(props: PromptProps) {
// Capture mode before it gets reset
const currentMode = store.mode
const variant = local.model.variant.current()
const currentAgent = local.agent.current()
if (!currentAgent) return

if (store.mode === "shell") {
sdk.client.session.shell({
sessionID,
agent: local.agent.current().name,
agent: currentAgent.name,
model: {
providerID: selectedModel.providerID,
modelID: selectedModel.modelID,
Expand All @@ -555,7 +557,7 @@ export function Prompt(props: PromptProps) {
sessionID,
command: command.slice(1),
arguments: args.join(" "),
agent: local.agent.current().name,
agent: currentAgent.name,
model: `${selectedModel.providerID}/${selectedModel.modelID}`,
messageID,
variant,
Expand All @@ -571,7 +573,7 @@ export function Prompt(props: PromptProps) {
sessionID,
...selectedModel,
messageID,
agent: local.agent.current().name,
agent: currentAgent.name,
model: selectedModel,
variant,
parts: [
Expand Down Expand Up @@ -691,7 +693,9 @@ export function Prompt(props: PromptProps) {
const highlight = createMemo(() => {
if (keybind.leader) return theme.border
if (store.mode === "shell") return theme.primary
return local.agent.color(local.agent.current().name)
const currentAgent = local.agent.current()
if (!currentAgent) return theme.secondary
return local.agent.color(currentAgent.name)
})

const showVariant = createMemo(() => {
Expand All @@ -702,7 +706,8 @@ export function Prompt(props: PromptProps) {
})

const spinnerDef = createMemo(() => {
const color = local.agent.color(local.agent.current().name)
const currentAgent = local.agent.current()
const color = currentAgent ? local.agent.color(currentAgent.name) : theme.secondary
return {
frames: createFrames({
color,
Expand Down Expand Up @@ -933,7 +938,7 @@ export function Prompt(props: PromptProps) {
/>
<box flexDirection="row" flexShrink={0} paddingTop={1} gap={1}>
<text fg={highlight()}>
{store.mode === "shell" ? "Shell" : Locale.titlecase(local.agent.current().name)}{" "}
{store.mode === "shell" ? "Shell" : Locale.titlecase(local.agent.current()?.name ?? "Agent")}{" "}
</text>
<Show when={store.mode === "normal"}>
<box flexDirection="row" gap={1}>
Expand Down
55 changes: 37 additions & 18 deletions packages/opencode/src/cli/cmd/tui/context/local.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,9 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
const agent = iife(() => {
const agents = createMemo(() => sync.data.agent.filter((x) => x.mode !== "subagent" && !x.hidden))
const [agentStore, setAgentStore] = createStore<{
current: string
current?: string
}>({
current: agents()[0].name,
current: agents()[0]?.name,
})
const { theme } = useTheme()
const colors = createMemo(() => [
Expand All @@ -54,23 +54,34 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
return agents()
},
current() {
return agents().find((x) => x.name === agentStore.current)!
const available = agents()
if (available.length === 0) return undefined
return available.find((x) => x.name === agentStore.current) ?? available[0]
},
set(name: string) {
if (!agents().some((x) => x.name === name))
return toast.show({
variant: "warning",
message: `Agent not found: ${name}`,
duration: 3000,
})
setAgentStore("current", name)
set(name: string | undefined) {
const available = agents()
if (available.length === 0) {
setAgentStore("current", undefined)
return
}
if (name && available.some((x) => x.name === name)) {
setAgentStore("current", name)
return
}
setAgentStore("current", available[0].name)
},
move(direction: 1 | -1) {
const available = agents()
if (available.length === 0) {
setAgentStore("current", undefined)
return
}
batch(() => {
let next = agents().findIndex((x) => x.name === agentStore.current) + direction
if (next < 0) next = agents().length - 1
if (next >= agents().length) next = 0
const value = agents()[next]
let next = available.findIndex((x) => x.name === agentStore.current) + direction
if (next < 0) next = available.length - 1
if (next >= available.length) next = 0
const value = available[next]
if (!value) return
setAgentStore("current", value.name)
})
},
Expand Down Expand Up @@ -179,6 +190,7 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({

const currentModel = createMemo(() => {
const a = agent.current()
if (!a) return undefined
return (
getFirstValidModel(
() => modelStore.model[a.name],
Expand Down Expand Up @@ -217,6 +229,8 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
}
}),
cycle(direction: 1 | -1) {
const currentAgent = agent.current()
if (!currentAgent) return
const current = currentModel()
if (!current) return
const recent = modelStore.recent
Expand All @@ -227,9 +241,11 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
if (next >= recent.length) next = 0
const val = recent[next]
if (!val) return
setModelStore("model", agent.current().name, { ...val })
setModelStore("model", currentAgent.name, { ...val })
},
cycleFavorite(direction: 1 | -1) {
const currentAgent = agent.current()
if (!currentAgent) return
const favorites = modelStore.favorite.filter((item) => isModelValid(item))
if (!favorites.length) {
toast.show({
Expand All @@ -253,7 +269,7 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
}
const next = favorites[index]
if (!next) return
setModelStore("model", agent.current().name, { ...next })
setModelStore("model", currentAgent.name, { ...next })
const uniq = uniqueBy([next, ...modelStore.recent], (x) => `${x.providerID}/${x.modelID}`)
if (uniq.length > 10) uniq.pop()
setModelStore(
Expand All @@ -264,6 +280,8 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
},
set(model: { providerID: string; modelID: string }, options?: { recent?: boolean }) {
batch(() => {
const currentAgent = agent.current()
if (!currentAgent) return
if (!isModelValid(model)) {
toast.show({
message: `Model ${model.providerID}/${model.modelID} is not valid`,
Expand All @@ -272,7 +290,7 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
})
return
}
setModelStore("model", agent.current().name, model)
setModelStore("model", currentAgent.name, model)
if (options?.recent) {
const uniq = uniqueBy([model, ...modelStore.recent], (x) => `${x.providerID}/${x.modelID}`)
if (uniq.length > 10) uniq.pop()
Expand Down Expand Up @@ -368,6 +386,7 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
// Automatically update model when agent changes
createEffect(() => {
const value = agent.current()
if (!value) return
if (value.model) {
if (isModelValid(value.model))
model.set({
Expand Down