fix: ask_user suggests tapping options without buttons (#137978)

* fix: avoid tap-only guidance in question prompts

* test: use public Teams API in question prompt contract

* test: assert question guidance through Teams rendering

Remove the intermediate presentation assertion so the regression exercises the
real card renderer before checking guidance. Retain the complete card and
plain-text expectations.

Co-authored-by: harshitgupta31415 <harshitgupta3.1415@gmail.com>

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
Co-authored-by: harshitgupta31415 <harshitgupta3.1415@gmail.com>
This commit is contained in:
HARSHIT GUPTA :) 2026-09-06 21:06:57 +05:30 • committed by GitHub
parent 10b82027b9
commit f80fb5b6e8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 57 additions and 5 deletions

View file

@ -486,7 +486,7 @@ describe("handleToolExecutionStart read path checks", () => {
"- Staging (Recommended): Safer default",
"- Production",
"",
"Tap an option, or reply with the option text or your own answer.",
"Reply with the number, the option text, or your own answer.",
].join("\n"),
},
{

View file

@ -109,16 +109,14 @@ function buildAgentHarnessQuestionPresentation(params: {
return undefined;
}
// The question stays in its own leading text block so reaction/native
// adapters can keep it while replacing the tap-only guidance below.
// adapters can keep it while replacing the reply guidance below.
const optionGuidance = [
...options.map(
(option) =>
`- ${formatText(option.label)}${option.description ? `: ${formatText(option.description)}` : ""}`,
),
"",
question.isOther
? "Tap an option, or reply with the option text or your own answer."
: "Tap an option, or reply with the option number or text.",
questionReplyGuidance(params.questions),
].join("\n");
return {
blocks: [

View file

@ -0,0 +1,54 @@
import { describe, expect, it } from "vitest";
import { msteamsPlugin } from "../../extensions/msteams/api.js";
import { buildAgentHarnessQuestionPromptPayload } from "../../src/agents/harness/user-input-bridge.js";
describe("question reply guidance", () => {
it.each([
{ isOther: false, guidance: "Reply with the number or option text." },
{
isOther: true,
guidance: "Reply with the number, the option text, or your own answer.",
},
])(
"remains actionable without question buttons (isOther=$isOther)",
async ({ isOther, guidance }) => {
const payload = buildAgentHarnessQuestionPromptPayload({
questionId: "question-1",
questions: [
{
id: "target",
header: "Target",
question: "Where should this deploy?",
options: [{ label: "Staging", description: "Safer default" }, { label: "Production" }],
isOther,
},
],
});
const presentation = payload.presentation;
if (!presentation) {
throw new Error("Expected a single-choice question presentation");
}
const optionText = ["- Staging: Safer default", "- Production", "", guidance].join("\n");
expect(payload.text).toContain(guidance);
// Teams supports buttons, but does not encode question actions as native controls.
const rendered = await msteamsPlugin.outbound?.renderPresentation?.({
payload,
presentation,
ctx: { cfg: {}, to: "conversation:test", text: payload.text, payload },
});
expect(rendered?.channelData?.msteams).toEqual({
presentationCard: {
type: "AdaptiveCard",
version: "1.4",
body: [
{ type: "TextBlock", text: payload.text, wrap: true },
{ type: "TextBlock", text: "Where should this deploy?", wrap: true },
{ type: "TextBlock", text: optionText, wrap: true },
],
},
});
},
);
});