From f80fb5b6e86d37e3e95021475e95d94c0ed6046e Mon Sep 17 00:00:00 2001 From: "HARSHIT GUPTA :)" Date: Sun, 6 Sep 2026 21:06:57 +0530 Subject: [PATCH] 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 --------- Co-authored-by: Peter Steinberger Co-authored-by: harshitgupta31415 --- ...ded-agent-subscribe.handlers.tools.test.ts | 2 +- src/agents/harness/user-input-bridge.ts | 6 +-- .../ask-user-msteams-presentation.test.ts | 54 +++++++++++++++++++ 3 files changed, 57 insertions(+), 5 deletions(-) create mode 100644 test/contracts/ask-user-msteams-presentation.test.ts diff --git a/src/agents/embedded-agent-subscribe.handlers.tools.test.ts b/src/agents/embedded-agent-subscribe.handlers.tools.test.ts index a781502b155e..2ec51ddce46f 100644 --- a/src/agents/embedded-agent-subscribe.handlers.tools.test.ts +++ b/src/agents/embedded-agent-subscribe.handlers.tools.test.ts @@ -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"), }, { diff --git a/src/agents/harness/user-input-bridge.ts b/src/agents/harness/user-input-bridge.ts index c4f882408593..dbefd8f4511a 100644 --- a/src/agents/harness/user-input-bridge.ts +++ b/src/agents/harness/user-input-bridge.ts @@ -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: [ diff --git a/test/contracts/ask-user-msteams-presentation.test.ts b/test/contracts/ask-user-msteams-presentation.test.ts new file mode 100644 index 000000000000..8ea65109b6ee --- /dev/null +++ b/test/contracts/ask-user-msteams-presentation.test.ts @@ -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 }, + ], + }, + }); + }, + ); +});