Bug: /good's toast notification corrupts the input box, using the officially documented client.tui.showToast() API
opencode version: 1.18.32
OS: Arch Linux (7.1.9-arch1-2)
TERM: xterm-256color, COLORTERM=truecolor
Description
A local plugin implements a custom slash command (/good) via the command.execute.before hook. On invocation, the hook:
- Sets
output.parts = [] (correctly suppresses the LLM turn per the documented Part[] contract for this hook)
- Runs some local bookkeeping (shell/sqlite calls)
- Calls
client.tui.showToast({ body: { title, message, variant: "success" } }) — the documented SDK method for /tui/show-toast
Every single invocation of this command visually corrupts/"butchers" the TUI's input box immediately after the toast fires. This reproduces 100% of the time, across fresh sessions (confirmed by fully killing and restarting the opencode process between attempts), ruling out stale plugin state.
What we ruled out
- Not caused by hot-reload/stale-plugin-state: reproduced on fully fresh process restarts.
- Not caused by weird bytes/control characters in the toast message: verified plain ASCII via
od -c, no embedded escape sequences.
- Not caused by using
output.parts to fake a synthetic assistant message: rewrote the plugin to use output.parts = [] (no synthetic message at all) plus the sanctioned client.tui.showToast() API instead — corruption still occurs identically.
- Not caused by the plugin's shell subprocess calls per se — the toast fires only after those complete, and the visual corruption appears specifically once the toast renders.
This points to the toast rendering/dismissal path in the TUI conflicting with the input box's own render cycle, specifically when the toast is triggered from within a command.execute.before hook (as opposed to a normal event hook context, e.g. session.idle, which is the only toast-trigger example shown in the docs).
Repro plugin
export const GoodPlugin = async ({ client }) => {
return {
"command.execute.before": async (input, output) => {
if (input.command !== "good") return;
output.parts = [];
await client.tui.showToast({
body: { title: "test", message: "hello", variant: "success" },
}).catch(() => {});
},
};
};
Paired with a minimal command file:
---
description: test
---
This command is handled entirely by the plugin. Do not execute anything.
Expected
Toast renders without disturbing the input box's layout/redraw state.
Actual
Input box becomes visually corrupted/garbled immediately after the toast is shown, every time, on this terminal setup.
Bug:
/good's toast notification corrupts the input box, using the officially documentedclient.tui.showToast()APIopencode version: 1.18.32
OS: Arch Linux (
7.1.9-arch1-2)TERM:
xterm-256color,COLORTERM=truecolorDescription
A local plugin implements a custom slash command (
/good) via thecommand.execute.beforehook. On invocation, the hook:output.parts = [](correctly suppresses the LLM turn per the documentedPart[]contract for this hook)client.tui.showToast({ body: { title, message, variant: "success" } })— the documented SDK method for/tui/show-toastEvery single invocation of this command visually corrupts/"butchers" the TUI's input box immediately after the toast fires. This reproduces 100% of the time, across fresh sessions (confirmed by fully killing and restarting the opencode process between attempts), ruling out stale plugin state.
What we ruled out
od -c, no embedded escape sequences.output.partsto fake a synthetic assistant message: rewrote the plugin to useoutput.parts = [](no synthetic message at all) plus the sanctionedclient.tui.showToast()API instead — corruption still occurs identically.This points to the toast rendering/dismissal path in the TUI conflicting with the input box's own render cycle, specifically when the toast is triggered from within a
command.execute.beforehook (as opposed to a normaleventhook context, e.g.session.idle, which is the only toast-trigger example shown in the docs).Repro plugin
Paired with a minimal command file:
Expected
Toast renders without disturbing the input box's layout/redraw state.
Actual
Input box becomes visually corrupted/garbled immediately after the toast is shown, every time, on this terminal setup.