diff --git a/packages/app/src/components/question-form-card-core.test.ts b/packages/app/src/components/question-form-card-core.test.ts index de076516d..7feb26821 100644 --- a/packages/app/src/components/question-form-card-core.test.ts +++ b/packages/app/src/components/question-form-card-core.test.ts @@ -76,4 +76,27 @@ describe("question form card core", () => { Response: "custom", }); }); + + test("shows text input for questions that allow other answers", () => { + const questions = parseQuestionFormQuestions({ + questions: [ + { + question: "Pick or type", + header: "Response", + options: [{ label: "A" }], + allowOther: true, + multiSelect: false, + }, + ], + }); + + if (!questions) throw new Error("questions did not parse"); + const [question] = questions; + if (!question) throw new Error("question missing"); + expect(questionShowsTextInput(question)).toBe(true); + expect(areQuestionsAnswered(questions, {}, { 0: "custom" })).toBe(true); + expect(buildQuestionFormAnswers(questions, {}, { 0: "custom" })).toEqual({ + Response: "custom", + }); + }); }); diff --git a/packages/server/src/server/agent/providers/opencode-agent.full-access.test.ts b/packages/server/src/server/agent/providers/opencode-agent.full-access.test.ts index 7ead93eb1..11b45dced 100644 --- a/packages/server/src/server/agent/providers/opencode-agent.full-access.test.ts +++ b/packages/server/src/server/agent/providers/opencode-agent.full-access.test.ts @@ -40,7 +40,7 @@ function toolPermissionEvent(): unknown { }; } -function questionEvent(): unknown { +function questionEvent(questionOverrides: Record = {}): unknown { return { type: "question.asked", properties: { @@ -51,6 +51,7 @@ function questionEvent(): unknown { question: "Which option should OpenCode use?", header: "Decision", options: [{ label: "Proceed", description: "Continue with the change" }], + ...questionOverrides, }, ], tool: { @@ -299,4 +300,54 @@ describe("OpenCode auto_accept feature", () => { await session.close(); }); + + test("surfaces OpenCode questions with a free-write answer option", async () => { + const { openCodeClient, runtime } = mockOpenCodeClient({ + events: [questionEvent({ custom: false }), idleEvent()], + }); + const receivedEvents: AgentStreamEvent[] = []; + + const client = new OpenCodeAgentClient(createTestLogger(), undefined, { runtime }); + const session = await client.createSession({ + provider: "opencode", + cwd: "/tmp/project", + }); + session.subscribe((event) => receivedEvents.push(event)); + + await session.run("Ask a question"); + + expect(receivedEvents.filter((event) => event.type === "permission_requested")).toEqual([ + expect.objectContaining({ + request: expect.objectContaining({ + id: "question-1", + kind: "question", + input: { + questions: [ + { + question: "Which option should OpenCode use?", + header: "Decision", + options: [{ label: "Proceed", description: "Continue with the change" }], + allowOther: true, + }, + ], + }, + }), + }), + ]); + + await session.respondToPermission("question-1", { + behavior: "allow", + updatedInput: { answers: { Decision: "Use another answer" } }, + }); + + expect(openCodeClient.calls.questionReply).toEqual([ + { + requestID: "question-1", + directory: "/tmp/project", + answers: [["Use another answer"]], + }, + ]); + + await session.close(); + }); }); diff --git a/packages/server/src/server/agent/providers/opencode-agent.ts b/packages/server/src/server/agent/providers/opencode-agent.ts index 977b4a387..798b9fa05 100644 --- a/packages/server/src/server/agent/providers/opencode-agent.ts +++ b/packages/server/src/server/agent/providers/opencode-agent.ts @@ -2521,6 +2521,7 @@ function appendOpenCodeQuestionAsked( header: q.header, options, ...(q.multiple === true ? { multiSelect: true } : {}), + allowOther: true, }, ]; });