You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The question tool's free-form answer ("Type your own answer…") cannot hold multi-line text, and pasting silently truncates it.
What happens: the custom-answer field is a TextInput (packages/app/src/session/requests/session-question-dock.tsx:718), which renders <input> (packages/ui/src/forms/text-input/text-input.tsx:6,67). An <input> cannot contain newlines, and the key handler commits on any Enter with no Shift+Enter branch (:748-750), so there is no keyboard path to a newline either.
Expected: a free-form answer should preserve line breaks. v2.0.21 used a <textarea> with Enter to commit and Shift+Enter for a newline (session-question-dock.tsx:676,693). The tool contract still describes free-form answers (packages/core/src/tool/plugin/question.ts:19) and Question.Answer is an unconstrained Array(String) (packages/schema/src/question.ts), so multi-line values are legal answers.
Suggested direction (not a design demand): keep Enter-to-commit and restore newline input for the custom answer only — a textarea or a multiline mode — with the pre-#54822 Shift+Enter behaviour.
Scope/status: still present on dev (501b15af26). I reproduced this in the web/desktop question dock (packages/app); I did not test the TUI.
Plugins
Custom tool.execute.after plugin for secret capture — it only rewrites the tool result and does not touch input handling; reproduces with plugins disabled.
OpenCode version
v2.0.32
Steps to reproduce
Trigger any question tool call (any tool that returns options).
Choose "Type your own answer…".
Paste a value that contains a newline, e.g. alpha⏎beta copied from an editor.
The field ends up as alphabeta (or only alpha) — the line break is silently lost.
Press Shift+Enter: it commits the answer instead of inserting a newline, so there is no way to type one either.
Thanks for the detailed report. I can confirm this in the web UI on v2.0.32, and it still happens on the latest dev (501b15af26).
What I did:
Had a simulated model make a question tool call.
Opened the session in a headless Chromium browser.
Used the Type your own answer… field.
What I saw:
The field renders a single-line <input type="text">.
Pasting alpha⏎beta gives alpha beta. Chromium turns the newline into a space, so the line break is lost.
Pressing Shift+Enter after typing first line doesn't insert a newline; the value stays first line. I didn't check whether it also submits the answer.
This matches the switch from the v2.0.21 <textarea>, which had Shift+Enter handling, to TextInput in #54822. The script below reproduces it, and the screenshot shows the pasted value.
Related, but not the same problem: #45230 (no way to turn off the custom-answer option), #33727 / #21143 (earlier Shift+Enter/IME fixes in the custom-answer textarea), and the TUI custom-answer issues #43516, #51294 and #30393.
Reproduction script (repro.sh)
#!/usr/bin/env bash# Reproduces #56895 in the web UI: the question tool's "Type your own answer…" field drops newlines.# Needs: opencode (v2) on PATH, opencode-drive, and playwright + chromium installed in $PW_DIR.# PW_DIR=/workspace/pw (bun add playwright && bunx playwright install --with-deps chromium)# Set OC_DEV=/path/to/opencode/checkout to run against a source checkout instead.set -e
export PATH=$HOME/.bun/bin:$PATH
PW_DIR=${PW_DIR:-/workspace/pw}if [ -n"$OC_DEV" ];then OC_CMD="bun --conditions=browser --preload=$OC_DEV/packages/tui/node_modules/@opentui/solid/scripts/preload.js $OC_DEV/packages/cli/src/index.ts";else OC_CMD=opencode;fi
mkdir -p /tmp/opencode
cat > /workspace/repro/serve-question.ts <<'DRIVE'// Drive part: starts OpenCode with a simulated model, makes it call the `question` tool,// writes the server URL to /tmp/opencode/url, then keeps the question pending until /tmp/opencode/done exists.import { Effect } from "effect"import { Llm, OpenCodeDriver } from "opencode-drive"import * as fs from "node:fs"export default OpenCodeDriver.use({ ...(process.env.OC_DEV ? { opencode: { dev: process.env.OC_DEV } } : {}) }, ({ ui, llm, opencode }) => Effect.gen(function* () { yield* llm.queue(Llm.toolCall({ index: 0, id: "call_q", name: "question", input: { questions: [{ question: "Which value?", header: "Value", options: [{ label: "One", description: "first" }] }] } }), Llm.finish("tool-calls")) yield* ui.submit("ask me") yield* ui.waitFor("Which value?") const info = yield* opencode.server.info() yield* Effect.promise(async () => { try { fs.unlinkSync("/tmp/opencode/done") } catch {} fs.writeFileSync("/tmp/opencode/url", info.urls[0]) for (let i = 0; i < 300 && !fs.existsSync("/tmp/opencode/done"); i++) await new Promise((r) => setTimeout(r, 500)) }) }))DRIVE
cat >"$PW_DIR/web.mjs"<<'WEB'import { chromium } from "playwright"const link = process.argv[2]const b = await chromium.launch(); const p = await b.newPage({ viewport: { width: 1200, height: 900 } })await p.goto(link); await p.waitForTimeout(6000)console.log("TEXT0", (await p.innerText("body")).slice(0, 300)); await p.screenshot({ path: "/workspace/repro/web-home.png" })await p.getByText("OpenCode Drive").first().click(); await p.waitForTimeout(2000)console.log("TEXT1", (await p.innerText("body")).slice(0, 400))const s = p.getByText("ask me").first(); if (await s.count()) { await s.click(); await p.waitForTimeout(2000) }await p.getByText("Which value?").first().waitFor({ timeout: 15000 })await p.screenshot({ path: "/workspace/repro/web-question.png" })const field = p.locator(".question-custom-field").first()const tag = await field.evaluate((el) => el.tagName)const box = tag === "INPUT" || tag === "TEXTAREA" ? field : field.locator("input,textarea").first()console.log("FIELD element:", tag, "->", await box.evaluate((el) => el.outerHTML.slice(0, 200)))await box.click()await p.keyboard.insertText("alpha\nbeta") // what a clipboard paste insertsawait p.waitForTimeout(300)console.log("VALUE after pasting 'alpha\\nbeta':", JSON.stringify(await box.inputValue()))await p.screenshot({ path: "/workspace/repro/web-pasted.png" })await box.fill("first line")await p.keyboard.press("Shift+Enter"); await p.waitForTimeout(800)const still = await box.inputValue({ timeout: 1000 }).catch(() => "<field gone>")console.log("VALUE after Shift+Enter:", JSON.stringify(still))await p.screenshot({ path: "/workspace/repro/web-shift-enter.png" })console.log("TEXT2", (await p.innerText("body")).slice(0, 600))await b.close()WEB
rm -f /tmp/opencode/url /tmp/opencode/done
(timeout 200 opencode-drive run /workspace/repro/serve-question.ts > /tmp/opencode/drive.log 2>&1&)
foriin$(seq 120);do [ -f /tmp/opencode/url ] &&break; sleep 1;done
PORT=$(sed -E 's#.*:([0-9]+).*#\1#' /tmp/opencode/url)
P=$(pgrep -f -- "--port $PORT"| head -1)# pair a browser with the drive's server (run `opencode pair` in the server's environment)
LINK=$(env $(tr '\0''\n'< /proc/$P/environ | grep -E "^(OPENCODE_CONFIG_DIR|OPENCODE_TEST_HOME|XDG_[A-Z_]+|OPENCODE_DRIVE)="| xargs) timeout 60 $OC_CMD pair 2>&1| grep -oE "http://[^ ]+/auth/connect/[A-Za-z0-9_-]+"| head -1)echo"LINK $LINK"
(cd "$PW_DIR"&& node web.mjs "$LINK")
touch /tmp/opencode/done
Run it with opencode-drive: opencode-drive run ./repro.sh. It drives opencode from your PATH; set OC_DEV to the path of an OpenCode checkout to run that from source instead.
Activity
opencode-agent commentedon Oct 18, 2026
Thanks for the detailed report. I can confirm this in the web UI on v2.0.32, and it still happens on the latest
dev(501b15af26).What I did:
questiontool call.What I saw:
<input type="text">.alpha⏎betagivesalpha beta. Chromium turns the newline into a space, so the line break is lost.first linedoesn't insert a newline; the value staysfirst line. I didn't check whether it also submits the answer.This matches the switch from the v2.0.21
<textarea>, which had Shift+Enter handling, toTextInputin #54822. The script below reproduces it, and the screenshot shows the pasted value.Related, but not the same problem: #45230 (no way to turn off the custom-answer option), #33727 / #21143 (earlier Shift+Enter/IME fixes in the custom-answer textarea), and the TUI custom-answer issues #43516, #51294 and #30393.
Reproduction script (repro.sh)
Run it with opencode-drive:
opencode-drive run ./repro.sh. It drivesopencodefrom yourPATH; setOC_DEVto the path of an OpenCode checkout to run that from source instead.