fix: pass custom answers through elaborate output

This commit is contained in:
Erik Kotsiuba
2026-04-28 22:35:43 +02:00
parent e8a36b72ef
commit 85962d985d
3 changed files with 109 additions and 5 deletions
+2 -1
View File
@@ -184,7 +184,8 @@ This document defines the stable external behavior. It does not explain internal
- after clarification, agents should prefer another structured follow-up over plain-text multiple choice when a decision is still unresolved - after clarification, agents should prefer another structured follow-up over plain-text multiple choice when a decision is still unresolved
- once prior answers narrow the branch, agents should bundle the next 2-3 related unresolved questions into one follow-up ask when possible, instead of using a long sequence of single-question asks - once prior answers narrow the branch, agents should bundle the next 2-3 related unresolved questions into one follow-up ask when possible, instead of using a long sequence of single-question asks
- `elaboration` is only present when `mode === "elaborate"` - `elaboration` is only present when `mode === "elaborate"`
- elaborate `content` text and transcript rendering describe each note directly using the full question prompt and option label, instead of a generic elaboration banner - elaborate `content` text and transcript rendering describe each note directly using the full question prompt and option label, and include the current committed answer text when available, instead of a generic elaboration banner
- when the user selects `Elaborate` without adding notes, elaborate `content` text and transcript rendering still include the committed answer text so the agent can elaborate on that answer directly
## Supported UX ## Supported UX
+28 -4
View File
@@ -71,16 +71,40 @@ export function formatElaborationLines(
): string[] { ): string[] {
const items = result.elaboration?.items ?? []; const items = result.elaboration?.items ?? [];
const lines = items.map((item) => { const lines = items.map((item) => {
const answerContext = formatElaborationAnswerContext(item.answer);
if (item.target.kind === "question") { if (item.target.kind === "question") {
return `User asked to elaborate on question ${quote(item.question.prompt)} with note ${quote(item.note)}`; return `User asked to elaborate on question ${quote(item.question.prompt)}${answerContext} with note ${quote(item.note)}`;
} }
if (!("option" in item)) { if (!("option" in item)) {
return `User asked to elaborate on question ${quote(item.question.prompt)} with note ${quote(item.note)}`; return `User asked to elaborate on question ${quote(item.question.prompt)}${answerContext} with note ${quote(item.note)}`;
} }
return `User asked to elaborate on question ${quote(item.question.prompt)} option ${quote(item.option.label)} with note ${quote(item.note)}`; return `User asked to elaborate on question ${quote(item.question.prompt)} option ${quote(item.option.label)}${answerContext} with note ${quote(item.note)}`;
}); });
return lines.length > 0 ? lines : [ELABORATED_SUMMARY]; if (lines.length > 0) {
return lines;
}
const answerLines = result.questions
.map((question) => {
const answer = result.answers[question.id];
return answer?.labels.length
? `User asked to elaborate on question ${quote(question.prompt)} after current answer ${quote(answer.labels.join(", "))}`
: undefined;
})
.filter((line): line is string => Boolean(line));
return answerLines.length > 0 ? answerLines : [ELABORATED_SUMMARY];
}
function formatElaborationAnswerContext(
answer: AskResult["answers"][string] | undefined
): string {
const labels = answer?.labels ?? [];
if (labels.length === 0) {
return "";
}
return ` after current answer ${quote(labels.join(", "))}`;
} }
function quote(value: string): string { function quote(value: string): string {
+79
View File
@@ -334,6 +334,85 @@ test("elaborate results keep committed answers alongside elaboration context", (
}, },
note: "Compare tradeoffs", note: "Compare tradeoffs",
}); });
assert.equal(
summarizeResult(result),
'User asked to elaborate on question "Pick a framework" after current answer "Vue" with note "Compare tradeoffs"'
);
assert.equal(
renderResultText(result),
'User asked to elaborate on question "Pick a framework" after current answer "Vue" with note "Compare tradeoffs"'
);
});
test("elaboration summary includes custom answers in note context", () => {
let state = createInitialState({
questions: [
{
id: "framework",
label: "Framework",
prompt: "Pick a framework",
options: [
{ value: "react", label: "React" },
{ value: "vue", label: "Vue" },
],
},
],
});
state = applyNumberShortcut(state, 3);
state = submitCustomAnswer(state, "SolidStart");
state = enterQuestionNoteMode(state, "framework");
state = saveNote(state, "Compare tradeoffs");
state = { ...state, mode: "elaborate" };
const result = toAskResult(state);
assert.equal(
summarizeResult(result),
'User asked to elaborate on question "Pick a framework" after current answer "SolidStart" with note "Compare tradeoffs"'
);
assert.equal(
renderResultText(result),
'User asked to elaborate on question "Pick a framework" after current answer "SolidStart" with note "Compare tradeoffs"'
);
assert.deepEqual(result.elaboration?.items[0]?.answer, {
values: ["SolidStart"],
labels: ["SolidStart"],
indices: [],
customText: "SolidStart",
note: "Compare tradeoffs",
optionNotes: undefined,
});
});
test("elaboration summary includes answered custom text even without notes", () => {
let state = createInitialState({
questions: [
{
id: "demo_goal",
label: "Goal",
prompt: "What kind of ask_user showcase would you like?",
options: [
{ value: "simple-demo", label: "Simple demo" },
{ value: "code-example", label: "Code example" },
],
},
],
});
state = applyNumberShortcut(state, 3);
state = submitCustomAnswer(state, "teeeel meee whyyyyy");
state = { ...state, mode: "elaborate" };
const result = toAskResult(state);
assert.equal(
summarizeResult(result),
'User asked to elaborate on question "What kind of ask_user showcase would you like?" after current answer "teeeel meee whyyyyy"'
);
assert.equal(
renderResultText(result),
'User asked to elaborate on question "What kind of ask_user showcase would you like?" after current answer "teeeel meee whyyyyy"'
);
assert.deepEqual(result.elaboration?.items, []);
}); });
test("elaborate continuation preserves unrelated committed answers", () => { test("elaborate continuation preserves unrelated committed answers", () => {