@@ -2,6 +2,7 @@ import { describe, expect, it } from "vitest";
22import { MAX_ISSUE_REQUEST_DEPTH } from "../index.js" ;
33import {
44 addIssueCommentSchema ,
5+ askUserQuestionsPayloadSchema ,
56 createIssueSchema ,
67 issueBlockedInboxAttentionSchema ,
78 resolveIssueRecoveryActionSchema ,
@@ -541,3 +542,71 @@ describe("issue validators", () => {
541542 expect ( parsed . success ) . toBe ( false ) ;
542543 } ) ;
543544} ) ;
545+
546+ describe ( "askUserQuestionsPayloadSchema" , ( ) => {
547+ it ( "normalizes the intuitive string-option / no-selectionMode shape agents send" , ( ) => {
548+ // The exact shape that previously returned 400 and never reached the user:
549+ // options as plain strings, selectionMode omitted (Claude's own tool shape).
550+ const parsed = askUserQuestionsPayloadSchema . safeParse ( {
551+ version : 1 ,
552+ questions : [
553+ {
554+ id : "deploy-approval" ,
555+ prompt : "Approve the staging deploy?" ,
556+ options : [ "Yes, ship it" , "No, hold" ] ,
557+ } ,
558+ ] ,
559+ } ) ;
560+
561+ expect ( parsed . success ) . toBe ( true ) ;
562+ if ( ! parsed . success ) return ;
563+ const question = parsed . data . questions [ 0 ] ! ;
564+ expect ( question . selectionMode ) . toBe ( "single" ) ;
565+ expect ( question . options ) . toEqual ( [
566+ { id : "yes-ship-it" , label : "Yes, ship it" } ,
567+ { id : "no-hold" , label : "No, hold" } ,
568+ ] ) ;
569+ } ) ;
570+
571+ it ( "accepts the canonical object-shaped payload unchanged" , ( ) => {
572+ const parsed = askUserQuestionsPayloadSchema . safeParse ( {
573+ version : 1 ,
574+ questions : [
575+ {
576+ id : "q1" ,
577+ prompt : "Pick one" ,
578+ selectionMode : "multi" ,
579+ options : [
580+ { id : "a" , label : "Option A" } ,
581+ { id : "b" , label : "Option B" } ,
582+ ] ,
583+ } ,
584+ ] ,
585+ } ) ;
586+
587+ expect ( parsed . success ) . toBe ( true ) ;
588+ if ( ! parsed . success ) return ;
589+ const question = parsed . data . questions [ 0 ] ! ;
590+ expect ( question . selectionMode ) . toBe ( "multi" ) ;
591+ expect ( question . options ) . toEqual ( [
592+ { id : "a" , label : "Option A" } ,
593+ { id : "b" , label : "Option B" } ,
594+ ] ) ;
595+ } ) ;
596+
597+ it ( "still rejects duplicate option ids after normalization" , ( ) => {
598+ const parsed = askUserQuestionsPayloadSchema . safeParse ( {
599+ version : 1 ,
600+ questions : [
601+ {
602+ id : "q1" ,
603+ prompt : "Pick one" ,
604+ // Both string options slugify to the same id.
605+ options : [ "Ship it" , "Ship it" ] ,
606+ } ,
607+ ] ,
608+ } ) ;
609+
610+ expect ( parsed . success ) . toBe ( false ) ;
611+ } ) ;
612+ } ) ;
0 commit comments