4.4 KiB
4.4 KiB
Ask tool contract
ask_user is a pi-native clarification tool for cases where implementation depends on user preference or missing requirements.
This document defines the stable external behavior. It does not explain internal helper-by-helper implementation.
Input
{
title?: string;
questions: Array<{
id: string;
label?: string;
prompt: string;
type?: "single" | "multi" | "preview";
required?: boolean;
options: Array<{
value: string;
label: string;
description?: string;
preview?: string;
}>;
}>;
}
Input rules
- at least one question is required
- every question must have non-empty trimmed
idandprompt - every question must have at least one option
- question ids must be unique within one tool call
- option
values must be unique within a question - optional
label,description, andpreviewfields must not be blank when provided labelfalls back toQ1,Q2, ...typedefaults tosinglerequireddefaults tofalserequiredis metadata only; it never blocks submission- preview questions require preview text for every option
- non-
previewquestions get an internalType your ownoption
Output
{
content: [{ type: "text"; text: string }];
details: {
title?: string;
cancelled: boolean;
questions: Array<{
id: string;
label: string;
prompt: string;
type: "single" | "multi" | "preview";
}>;
answers: Record<
string,
{
values: string[];
labels: string[];
indices: number[];
customText?: string;
note?: string;
optionNotes?: Record<string, string>;
}
>;
};
}
Output rules
cancelled: truemeans the user dismissed the flow, UI was unavailable, or the payload was invalid before UI opened- unanswered questions are omitted from
answers - single-select answers still use arrays
indicesare 1-based rendered option positionscustomTextstores the free-form answer- on single-select questions, saving free-form text clears selected options for that question
- on multi-select questions,
valuesandlabelsinclude both selected options andcustomTextwhen both are present - on multi-select questions, selected options keep their original order and
customTextis appended last - saving or clearing free-form text on a multi-select question does not clear other selected options
notestores a question-level noteoptionNotesincludes only notes for selected options- question notes may exist without a selected answer
Supported UX
- tabbed multi-question flow
- single-select, multi-select, and preview questions
- inline free-form answers for non-preview questions
- pi-style
@file path autocomplete inside free-form answer and note editors - question notes via
Shift+N - option notes via
n - number-key quick selection
- submit/cancel review tab
- transcript-friendly call and result rendering
Keyboard behavior
Main flow:
Tab,Shift+Tab,Left,Right: move between tabsUp,Down: move between optionsEnter: confirm or submitEsc: cancel the flowCtrl+C: dismiss the entire flow immediately, even while editing a note or free-form answer1..9: select or toggle the matching optionSpace: toggle the active optionn: edit the active option noteShift+N: edit the current question note
Editing flow:
Enter: save and submit the current inputEsc: save draft and close the editorCtrl+C: dismiss the entire flow immediately without saving the current editor draft- when editor has text, arrow keys and
Tabstay in the editor so the cursor can move while typing - when editor is empty,
Up/Downmove options andTab/Shift+Tab/Left/Rightmove between tabs without requiringEscfirst - navigation resumes only after closing the editor with
Esc, unless the editor is empty and the navigation keys above are used
Non-interactive mode
If ctx.hasUI === false, the tool returns a Needs user input message in content and a cancelled result in details.
The fallback message includes normalized pending questions and options so the caller can re-ask them manually. details.questions still contains normalized question metadata, while details.answers stays empty until a user responds.
Source of truth
Behavior should be verified against:
src/types.tsand exported state/result helperstests/*.test.ts- this contract