Files
hapi/web/src/components/ToolCard/askUserQuestion.ts
T
weishu ac67718ec0 feat: implement AskUserQuestion tool with interactive UI and question flow
Add comprehensive support for the AskUserQuestion tool including:
- New footer component for handling user responses with multi-step question flow
- Type definitions and parsing utilities for question and answer formats
- View component for displaying questions and options
- Integration with permission system to capture user answers
- Support for single and multi-select questions with optional text input
- Haptic feedback and error handling for better UX
2025-12-18 23:24:41 +08:00

81 lines
2.6 KiB
TypeScript

export type AskUserQuestionOption = {
label: string
description: string | null
}
export type AskUserQuestionQuestion = {
header: string | null
question: string
options: AskUserQuestionOption[]
multiSelect: boolean
}
export type AskUserQuestionQuestionInfo = {
header: string | null
question: string | null
}
function isObject(value: unknown): value is Record<string, unknown> {
return Boolean(value) && typeof value === 'object'
}
export function isAskUserQuestionToolName(toolName: string): boolean {
return toolName === 'AskUserQuestion' || toolName === 'ask_user_question'
}
export function parseAskUserQuestionInput(input: unknown): { questions: AskUserQuestionQuestion[] } {
if (!isObject(input)) return { questions: [] }
const rawQuestions = input.questions
if (!Array.isArray(rawQuestions)) return { questions: [] }
const questions: AskUserQuestionQuestion[] = []
for (const raw of rawQuestions) {
if (!isObject(raw)) continue
const question = typeof raw.question === 'string' ? raw.question.trim() : ''
const header = typeof raw.header === 'string' ? raw.header.trim() : ''
const multiSelect = typeof raw.multiSelect === 'boolean' ? raw.multiSelect : false
const rawOptions = Array.isArray(raw.options) ? raw.options : []
const options: AskUserQuestionOption[] = []
for (const opt of rawOptions) {
if (!isObject(opt)) continue
const label = typeof opt.label === 'string' ? opt.label.trim() : ''
if (!label) continue
const description = typeof opt.description === 'string' ? opt.description.trim() : null
options.push({ label, description })
}
if (!question && options.length === 0) continue
questions.push({
header: header.length > 0 ? header : null,
question,
options,
multiSelect
})
}
return { questions }
}
export function extractAskUserQuestionQuestionsInfo(input: unknown): AskUserQuestionQuestionInfo[] | null {
if (!isObject(input)) return null
const raw = input.questions
if (!Array.isArray(raw)) return null
const questions: AskUserQuestionQuestionInfo[] = []
for (const q of raw) {
if (!isObject(q)) continue
const header = typeof q.header === 'string' ? q.header.trim() : null
const question = typeof q.question === 'string' ? q.question.trim() : null
questions.push({
header: header && header.length > 0 ? header : null,
question: question && question.length > 0 ? question : null
})
}
return questions
}