summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorBowen Dwelle <[email protected]>2026-01-17 23:41:36 -0700
committerGitHub <[email protected]>2026-01-18 00:41:36 -0600
commit3591372c45a8cfb3114d0f221f4ea8d51c527103 (patch)
tree15c001ac3d9983b7c55e8c78a5a9c49d777dc728
parent90f848fbc697c6ec5c52edc84bbde33c0fa6a560 (diff)
downloadopencode-3591372c45a8cfb3114d0f221f4ea8d51c527103.tar.gz
opencode-3591372c45a8cfb3114d0f221f4ea8d51c527103.zip
feat(tool): increase question header and label limits (#9201)
-rw-r--r--packages/opencode/src/question/index.ts4
-rw-r--r--packages/opencode/test/tool/question.test.ts107
2 files changed, 109 insertions, 2 deletions
diff --git a/packages/opencode/src/question/index.ts b/packages/opencode/src/question/index.ts
index d18098a9c..41029ecbb 100644
--- a/packages/opencode/src/question/index.ts
+++ b/packages/opencode/src/question/index.ts
@@ -10,7 +10,7 @@ export namespace Question {
export const Option = z
.object({
- label: z.string().describe("Display text (1-5 words, concise)"),
+ label: z.string().max(30).describe("Display text (1-5 words, concise)"),
description: z.string().describe("Explanation of choice"),
})
.meta({
@@ -21,7 +21,7 @@ export namespace Question {
export const Info = z
.object({
question: z.string().describe("Complete question"),
- header: z.string().max(12).describe("Very short label (max 12 chars)"),
+ header: z.string().max(30).describe("Very short label (max 30 chars)"),
options: z.array(Option).describe("Available choices"),
multiple: z.boolean().optional().describe("Allow selecting multiple choices"),
custom: z.boolean().optional().describe("Allow typing a custom answer (default: true)"),
diff --git a/packages/opencode/test/tool/question.test.ts b/packages/opencode/test/tool/question.test.ts
new file mode 100644
index 000000000..9e3f4e25c
--- /dev/null
+++ b/packages/opencode/test/tool/question.test.ts
@@ -0,0 +1,107 @@
+import { describe, expect, test, spyOn, beforeEach, afterEach } from "bun:test"
+import { z } from "zod"
+import { QuestionTool } from "../../src/tool/question"
+import * as QuestionModule from "../../src/question"
+
+const ctx = {
+ sessionID: "test-session",
+ messageID: "test-message",
+ callID: "test-call",
+ agent: "test-agent",
+ abort: AbortSignal.any([]),
+ metadata: () => {},
+ ask: async () => {},
+}
+
+describe("tool.question", () => {
+ let askSpy: any;
+
+ beforeEach(() => {
+ askSpy = spyOn(QuestionModule.Question, "ask").mockImplementation(async () => {
+ return []
+ })
+ })
+
+ afterEach(() => {
+ askSpy.mockRestore()
+ })
+
+ test("should successfully execute with valid question parameters", async () => {
+ const tool = await QuestionTool.init()
+ const questions = [
+ {
+ question: "What is your favorite color?",
+ header: "Color",
+ options: [
+ { label: "Red", description: "The color of passion" },
+ { label: "Blue", description: "The color of sky" },
+ ],
+ multiple: false,
+ },
+ ]
+
+ askSpy.mockResolvedValueOnce([["Red"]])
+
+ const result = await tool.execute(
+ { questions },
+ ctx,
+ )
+ expect(askSpy).toHaveBeenCalledTimes(1)
+ expect(result.title).toBe("Asked 1 question")
+ })
+
+ test("should now pass with a header longer than 12 but less than 30 chars", async () => {
+ const tool = await QuestionTool.init()
+ const questions = [
+ {
+ question: "What is your favorite animal?",
+ header: "This Header is Over 12",
+ options: [{ label: "Dog", description: "Man's best friend" }],
+ },
+ ]
+
+ askSpy.mockResolvedValueOnce([["Dog"]])
+
+ const result = await tool.execute({ questions }, ctx)
+ expect(result.output).toContain(`"What is your favorite animal?"="Dog"`)
+ })
+
+ test("should throw an Error for header exceeding 30 characters", async () => {
+ const tool = await QuestionTool.init()
+ const questions = [
+ {
+ question: "What is your favorite animal?",
+ header: "This Header is Definitely More Than Thirty Characters Long",
+ options: [{ label: "Dog", description: "Man's best friend" }],
+ },
+ ]
+ try {
+ await tool.execute({ questions }, ctx)
+ // If it reaches here, the test should fail
+ expect(true).toBe(false)
+ } catch (e: any) {
+ expect(e).toBeInstanceOf(Error)
+ expect(e.cause).toBeInstanceOf(z.ZodError)
+ }
+ })
+
+ test("should throw an Error for label exceeding 30 characters", async () => {
+ const tool = await QuestionTool.init()
+ const questions = [
+ {
+ question: "A question with a very long label",
+ header: "Long Label",
+ options: [{ label: "This is a very, very, very long label that will exceed the limit", description: "A description" }],
+ },
+ ]
+ try {
+ await tool.execute({ questions }, ctx)
+ // If it reaches here, the test should fail
+ expect(true).toBe(false)
+ } catch (e: any) {
+ expect(e).toBeInstanceOf(Error)
+ expect(e.cause).toBeInstanceOf(z.ZodError)
+ }
+ })
+})
+