diff --git a/packages/opencode/src/cli/cmd/tui/context/sync.tsx b/packages/opencode/src/cli/cmd/tui/context/sync.tsx
index b6e36b1953..a42b4c20ba 100644
--- a/packages/opencode/src/cli/cmd/tui/context/sync.tsx
+++ b/packages/opencode/src/cli/cmd/tui/context/sync.tsx
@@ -54,9 +54,6 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
session_status: {
[sessionID: string]: SessionStatus
}
- suggest_debug: {
- [sessionID: string]: { state: string; detail?: string; time: number }
- }
session_diff: {
[sessionID: string]: Snapshot.FileDiff[]
}
@@ -98,7 +95,6 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
provider_default: {},
session: [],
session_status: {},
- suggest_debug: {},
session_diff: {},
todo: {},
message: {},
@@ -237,16 +233,7 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
}
case "session.status": {
- setStore("session_status", event.properties.sessionID, event.properties.status)
- break
- }
-
- case "session.suggest_debug": {
- setStore("suggest_debug", event.properties.sessionID, {
- state: event.properties.state,
- detail: event.properties.detail,
- time: Date.now(),
- })
+ setStore("session_status", event.properties.sessionID, reconcile(event.properties.status))
break
}
diff --git a/packages/opencode/src/cli/cmd/tui/feature-plugins/sidebar/suggest.tsx b/packages/opencode/src/cli/cmd/tui/feature-plugins/sidebar/suggest.tsx
deleted file mode 100644
index 7a6f543d21..0000000000
--- a/packages/opencode/src/cli/cmd/tui/feature-plugins/sidebar/suggest.tsx
+++ /dev/null
@@ -1,71 +0,0 @@
-import type { TuiPlugin, TuiPluginApi, TuiPluginModule } from "@opencode-ai/plugin/tui"
-import { createMemo, createSignal, onCleanup } from "solid-js"
-
-const id = "internal:sidebar-suggest"
-
-function View(props: { api: TuiPluginApi; session_id: string }) {
- const theme = () => props.api.theme.current
- const debug = createMemo(
- () =>
- (props.api.state.session as any).suggestDebug(props.session_id) as
- | { state: string; detail?: string; time: number }
- | undefined,
- )
-
- const [now, setNow] = createSignal(Date.now())
- const timer = setInterval(() => setNow(Date.now()), 1000)
- onCleanup(() => clearInterval(timer))
-
- const age = createMemo(() => {
- const d = debug()
- if (!d) return ""
- const ms = now() - d.time
- if (ms < 1000) return "just now"
- return `${Math.floor(ms / 1000)}s ago`
- })
-
- const color = createMemo(() => {
- const state = debug()?.state
- if (state === "generating") return theme().brand
- if (state === "done") return theme().textSuccess ?? "green"
- if (state === "error") return theme().textDanger ?? "red"
- if (state === "refused") return theme().textWarning ?? "yellow"
- return theme().textMuted
- })
-
- return (
-
-
- Suggest
-
- {debug() ? (
- <>
-
- {debug()!.state} {age()}
-
- {debug()!.detail ? {debug()!.detail!.slice(0, 38)} : null}
- >
- ) : (
- waiting
- )}
-
- )
-}
-
-const tui: TuiPlugin = async (api) => {
- api.slots.register({
- order: 50,
- slots: {
- sidebar_content(_ctx, props) {
- return
- },
- },
- })
-}
-
-const plugin: TuiPluginModule & { id: string } = {
- id,
- tui,
-}
-
-export default plugin
diff --git a/packages/opencode/src/cli/cmd/tui/plugin/api.tsx b/packages/opencode/src/cli/cmd/tui/plugin/api.tsx
index 53e690f3ec..529c50cfa3 100644
--- a/packages/opencode/src/cli/cmd/tui/plugin/api.tsx
+++ b/packages/opencode/src/cli/cmd/tui/plugin/api.tsx
@@ -173,9 +173,6 @@ function stateApi(sync: ReturnType): TuiPluginApi["state"] {
status(sessionID) {
return sync.data.session_status[sessionID]
},
- suggestDebug(sessionID) {
- return sync.data.suggest_debug[sessionID]
- },
permission(sessionID) {
return sync.data.permission[sessionID] ?? []
},
diff --git a/packages/opencode/src/cli/cmd/tui/plugin/internal.ts b/packages/opencode/src/cli/cmd/tui/plugin/internal.ts
index 5ffc6ffaa5..856ee0ebb1 100644
--- a/packages/opencode/src/cli/cmd/tui/plugin/internal.ts
+++ b/packages/opencode/src/cli/cmd/tui/plugin/internal.ts
@@ -6,7 +6,6 @@ import SidebarLsp from "../feature-plugins/sidebar/lsp"
import SidebarTodo from "../feature-plugins/sidebar/todo"
import SidebarFiles from "../feature-plugins/sidebar/files"
import SidebarFooter from "../feature-plugins/sidebar/footer"
-import SidebarSuggest from "../feature-plugins/sidebar/suggest"
import PluginManager from "../feature-plugins/system/plugins"
import type { TuiPlugin, TuiPluginModule } from "@opencode-ai/plugin/tui"
@@ -18,7 +17,6 @@ export type InternalTuiPlugin = TuiPluginModule & {
export const INTERNAL_TUI_PLUGINS: InternalTuiPlugin[] = [
HomeFooter,
HomeTips,
- SidebarSuggest,
SidebarContext,
SidebarMcp,
SidebarLsp,
diff --git a/packages/opencode/src/session/prompt.ts b/packages/opencode/src/session/prompt.ts
index 68a7ca4b66..87e7be643f 100644
--- a/packages/opencode/src/session/prompt.ts
+++ b/packages/opencode/src/session/prompt.ts
@@ -250,9 +250,6 @@ export namespace SessionPrompt {
)
})
- const suggestDebug = (sessionID: SessionID, state: SessionStatus.SuggestState, detail?: string) =>
- bus.publish(SessionStatus.Event.SuggestDebug, { sessionID, state, detail })
-
const suggest = Effect.fn("SessionPrompt.suggest")(function* (input: {
session: Session.Info
sessionID: SessionID
@@ -275,8 +272,6 @@ export namespace SessionPrompt {
const ag = yield* agents.get(message.agent ?? "code")
if (!ag) return
- yield* suggestDebug(input.sessionID, "generating")
-
// Full message history so the cached KV from the main conversation is reused
const msgs = yield* MessageV2.filterCompactedEffect(input.sessionID)
const real = (item: MessageV2.WithParts) =>
@@ -292,7 +287,7 @@ export namespace SessionPrompt {
const modelMsgs = yield* Effect.promise(() => MessageV2.toModelMessages(msgs, model))
const system = [...env, ...(skills ? [skills] : []), ...instructions]
- const exit = yield* Effect.promise(async (signal) => {
+ const text = yield* Effect.promise(async (signal) => {
const result = await LLM.stream({
agent: ag,
user,
@@ -308,14 +303,7 @@ export namespace SessionPrompt {
messages: [...modelMsgs, { role: "user" as const, content: PROMPT_SUGGEST_NEXT }],
})
return result.text
- }).pipe(Effect.exit)
-
- if (Exit.isFailure(exit)) {
- const err = Cause.squash(exit.cause)
- yield* suggestDebug(input.sessionID, "error", err instanceof Error ? err.message : String(err))
- return
- }
- const text = exit.value
+ })
const line = text
.replace(/[\s\S]*?<\/think>\s*/g, "")
@@ -323,24 +311,17 @@ export namespace SessionPrompt {
.map((item) => item.trim())
.find((item) => item.length > 0)
?.replace(/^["'`]+|["'`]+$/g, "")
- if (!line) {
- yield* suggestDebug(input.sessionID, "refused", "empty response")
- return
- }
+ if (!line) return
const tag = line
.toUpperCase()
.replace(/[\s-]+/g, "_")
.replace(/[^A-Z_]/g, "")
- if (tag === "NO_SUGGESTION") {
- yield* suggestDebug(input.sessionID, "refused", "NO_SUGGESTION")
- return
- }
+ if (tag === "NO_SUGGESTION") return
- const suggestion = line.length > 240 ? line.slice(0, 237) + "..." : line
+ const suggestion = line.length > 110 ? line.slice(0, 107) + "..." : line
if ((yield* status.get(input.sessionID)).type !== "idle") return
yield* status.suggest(input.sessionID, suggestion)
- yield* suggestDebug(input.sessionID, "done", suggestion)
})
const insertReminders = Effect.fn("SessionPrompt.insertReminders")(function* (input: {
@@ -1414,13 +1395,11 @@ NOTE: At any point in time through this workflow you should feel free to ask the
if (input.noReply === true) return message
const result = yield* loop({ sessionID: input.sessionID })
- if (Flag.OPENCODE_EXPERIMENTAL_NEXT_PROMPT) {
- yield* suggest({
- session,
- sessionID: input.sessionID,
- message: result,
- }).pipe(Effect.ignore, Effect.forkIn(scope))
- }
+ yield* suggest({
+ session,
+ sessionID: input.sessionID,
+ message: result,
+ }).pipe(Effect.ignore, Effect.forkIn(scope))
return result
},
)
diff --git a/packages/opencode/src/session/prompt/suggest-next.txt b/packages/opencode/src/session/prompt/suggest-next.txt
index ebcb3e5d9f..dbb2756dc1 100644
--- a/packages/opencode/src/session/prompt/suggest-next.txt
+++ b/packages/opencode/src/session/prompt/suggest-next.txt
@@ -4,7 +4,7 @@ Goal:
- Suggest a useful next step that keeps momentum.
Rules:
-- Output exactly one line.
+- Output exactly one line, 110 characters max. Be concise.
- Write as the user speaking to the assistant (for example: "Can you...", "Help me...", "Let's...").
- Match the user's tone and language; keep it natural and human.
- Prefer a concrete action over a broad question.
diff --git a/packages/opencode/src/session/status.ts b/packages/opencode/src/session/status.ts
index bc31792d2e..6a85c77127 100644
--- a/packages/opencode/src/session/status.ts
+++ b/packages/opencode/src/session/status.ts
@@ -28,9 +28,6 @@ export namespace SessionStatus {
})
export type Info = z.infer
- export const SuggestState = z.enum(["generating", "done", "refused", "error"])
- export type SuggestState = z.infer
-
export const Event = {
Status: BusEvent.define(
"session.status",
@@ -39,14 +36,6 @@ export namespace SessionStatus {
status: Info,
}),
),
- SuggestDebug: BusEvent.define(
- "session.suggest_debug",
- z.object({
- sessionID: SessionID.zod,
- state: SuggestState,
- detail: z.string().optional(),
- }),
- ),
// deprecated
Idle: BusEvent.define(
"session.idle",