diff --git a/packages/app/src/pages/session/composer/session-question-dock.tsx b/packages/app/src/pages/session/composer/session-question-dock.tsx index 6eda5f0bc5..b909d8c932 100644 --- a/packages/app/src/pages/session/composer/session-question-dock.tsx +++ b/packages/app/src/pages/session/composer/session-question-dock.tsx @@ -73,10 +73,11 @@ export const SessionQuestionDock: Component<{ request: QuestionRequest; onSubmit const total = createMemo(() => questions().length) const cached = cache.get(cacheKey) + const defaults = props.request.questions.map((q) => q.default ?? "") const [store, setStore] = createStore({ tab: cached?.tab ?? 0, answers: cached?.answers ?? ([] as QuestionAnswer[]), - custom: cached?.custom ?? ([] as string[]), + custom: cached?.custom ?? defaults, customOn: cached?.customOn ?? ([] as boolean[]), editing: false, focus: 0, diff --git a/packages/core/src/tool/question.ts b/packages/core/src/tool/question.ts index 3fa5b3b215..97fba1e5ba 100644 --- a/packages/core/src/tool/question.ts +++ b/packages/core/src/tool/question.ts @@ -21,7 +21,8 @@ Usage notes: - Every question is a card: a choice question (default) lists options; a free-form question — one expecting an open-ended typed answer, like a name or a number — uses kind: "text" and renders a bare text input with submit, so give it no options - When \`custom\` is enabled (default), a "Type your own answer" option is added automatically; don't include "Other" or catch-all options - Answers are returned as arrays of labels; set \`multiple: true\` to allow selecting more than one -- If you recommend a specific option, make that the first option in the list and add "(Recommended)" at the end of the label` +- If you recommend a specific option, make that the first option in the list and add "(Recommended)" at the end of the label +- For text-kind questions, set \`default\` to pre-fill the input with a value the user can edit before submitting` export const Input = Schema.Struct({ questions: Schema.Array(QuestionV2.Prompt).annotate({ description: "Questions to ask" }), diff --git a/packages/core/test/tool-question.test.ts b/packages/core/test/tool-question.test.ts index 540bae03b5..e7a216cd3f 100644 --- a/packages/core/test/tool-question.test.ts +++ b/packages/core/test/tool-question.test.ts @@ -165,4 +165,30 @@ describe("QuestionTool", () => { expect(Exit.isFailure(exit)).toBe(true) }), ) + + it.effect("passes a default field through on text-kind questions for pre-filling", () => + Effect.gen(function* () { + assertions.length = 0 + captured = undefined + reject = false + deny = false + const registry = yield* ToolRegistry.Service + const questions = [ + { + question: "Edit your description", + header: "Description", + kind: "text" as const, + options: [], + default: "JJ is a researcher focused on quantum control.", + }, + ] + + yield* settleTool(registry, { + sessionID, + ...toolIdentity, + call: { type: "tool-call", id: "call-question-default", name: "question", input: { questions } }, + }) + expect(capturedInput()?.questions[0].default).toBe("JJ is a researcher focused on quantum control.") + }), + ) }) diff --git a/packages/schema/src/question.ts b/packages/schema/src/question.ts index 0f44af7685..a6867918c9 100644 --- a/packages/schema/src/question.ts +++ b/packages/schema/src/question.ts @@ -36,6 +36,9 @@ const base = { options: Schema.Array(Option).annotate({ description: "Available choices" }), multiple: Schema.Boolean.pipe(optional).annotate({ description: "Allow selecting multiple choices" }), kind: Kind.pipe(optional), + default: Schema.String.pipe(optional).annotate({ + description: "Pre-filled value for text-kind questions (user can edit before submitting)", + }), } export const Info = Schema.Struct({ diff --git a/packages/schema/src/v1/question.ts b/packages/schema/src/v1/question.ts index b90503a594..08b9401caf 100644 --- a/packages/schema/src/v1/question.ts +++ b/packages/schema/src/v1/question.ts @@ -27,6 +27,9 @@ const base = { options: Schema.Array(Option).annotate({ description: "Available choices" }), multiple: Schema.optional(Schema.Boolean).annotate({ description: "Allow selecting multiple choices" }), kind: Schema.optional(Kind), + default: Schema.optional(Schema.String).annotate({ + description: "Pre-filled value for text-kind questions (user can edit before submitting)", + }), } export const Info = Schema.Struct({ diff --git a/packages/sdk/js/src/v2/gen/types.gen.ts b/packages/sdk/js/src/v2/gen/types.gen.ts index 37ff1cb916..fb467d191e 100644 --- a/packages/sdk/js/src/v2/gen/types.gen.ts +++ b/packages/sdk/js/src/v2/gen/types.gen.ts @@ -738,6 +738,7 @@ export type QuestionInfo = { * Question shape: "choice" (default, an option list) or "text" (a free-form text card, no options) */ kind?: "choice" | "text" + default?: string custom?: boolean } @@ -3077,6 +3078,7 @@ export type ModelRef = { export type LocationRef = { directory: string + directories?: Array workspaceID?: string } @@ -3197,6 +3199,7 @@ export type QuestionV2Info = { * Question shape: "choice" (default, an option list) or "text" (a free-form text card, no options) */ kind?: "choice" | "text" + default?: string custom?: boolean } diff --git a/packages/tui/src/routes/session/question.tsx b/packages/tui/src/routes/session/question.tsx index 7921b3a1e6..10187dbf5a 100644 --- a/packages/tui/src/routes/session/question.tsx +++ b/packages/tui/src/routes/session/question.tsx @@ -25,7 +25,7 @@ export function QuestionPrompt(props: { request: QuestionRequest; directory?: st const [store, setStore] = createStore({ tab: 0, answers: [] as QuestionAnswer[], - custom: [] as string[], + custom: props.request.questions.map((q) => q.default ?? "") as string[], selected: 0, editing: false, })