From 86df0587ba00a71b41db756528c685db27390a39 Mon Sep 17 00:00:00 2001 From: "paseo-bot[bot]" <266920839+paseo-bot[bot]@users.noreply.github.com> Date: Tue, 29 Sep 2026 16:00:31 +0200 Subject: [PATCH] fix(opencode): show the question text and typed answer in v2 question cards (#5679) OpenCode v2 sends each question as a form field whose title is the short header, whose description is the question, and whose custom flag allows a typed answer. The mapper used the field key as the header and the title as the question, and dropped custom, so the card showed only the header and had no answer field. Closes #5667 Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com> (cherry picked from commit 2838922ade0b7e8658e19db77456f442d00a1c27) --- .../providers/opencode/v2/permissions.test.ts | 65 +++++++++++++++++++ .../providers/opencode/v2/permissions.ts | 5 +- 2 files changed, 68 insertions(+), 2 deletions(-) diff --git a/packages/server/src/server/agent/providers/opencode/v2/permissions.test.ts b/packages/server/src/server/agent/providers/opencode/v2/permissions.test.ts index 451bdf81c6..50a6d98549 100644 --- a/packages/server/src/server/agent/providers/opencode/v2/permissions.test.ts +++ b/packages/server/src/server/agent/providers/opencode/v2/permissions.test.ts @@ -66,6 +66,71 @@ describe("OpenCode v2 questions", () => { }); }); +describe("OpenCode v2 question tool", () => { + test("shows the question text and accepts a typed answer", async () => { + const harness = new V2Harness(); + harness.api.session.form.list = async () => [ + { + id: "question", + sessionID: "session", + title: "Questions", + fields: [ + { + key: "q0", + title: "Next task", + description: "What would you like me to work on next?", + type: "string", + options: [ + { value: "Explore", label: "Explore", description: "Map the codebase" }, + { value: "Fix a bug", label: "Fix a bug", description: "Track down a bug" }, + ], + custom: true, + }, + ], + }, + ]; + const answers: Parameters[0][] = []; + harness.api.session.form.reply = async (input) => { + answers.push(input); + }; + const client = new OpenCodeV2AgentClient({ + logger: createTestLogger(), + runtime: harness.runtime, + }); + const session = await client.createSession({ provider: "opencode", cwd: "/tmp/project" }); + try { + const [request] = session.getPendingPermissions(); + expect(request.input).toEqual({ + questions: [ + { + header: "Next task", + question: "What would you like me to work on next?", + options: [ + { value: "Explore", label: "Explore", description: "Map the codebase" }, + { value: "Fix a bug", label: "Fix a bug", description: "Track down a bug" }, + ], + multiple: false, + allowOther: true, + }, + ], + }); + await session.respondToPermission("question", { + behavior: "allow", + updatedInput: { answers: { "Next task": "Write the release notes" } }, + }); + expect(answers).toEqual([ + { + sessionID: "session", + formID: "question", + answer: { q0: "Write the release notes" }, + }, + ]); + } finally { + await session.close(); + } + }); +}); + describe("OpenCode v2 permission routing", () => { test("routes a child approval back to its owning session", async () => { const harness = new V2Harness(); diff --git a/packages/server/src/server/agent/providers/opencode/v2/permissions.ts b/packages/server/src/server/agent/providers/opencode/v2/permissions.ts index fcd0213282..176fc455be 100644 --- a/packages/server/src/server/agent/providers/opencode/v2/permissions.ts +++ b/packages/server/src/server/agent/providers/opencode/v2/permissions.ts @@ -100,10 +100,11 @@ export class SessionPermissions { title: form.title, input: { questions: form.fields.map((field) => ({ - header: field.key, - question: field.title ?? field.key, + header: field.title ?? field.key, + question: field.description ?? field.title ?? field.key, options: "options" in field ? field.options : undefined, multiple: field.type === "multiselect", + allowOther: "custom" in field && field.custom === true, })), }, };