@agimon-ai/po-user-feedback 0.0.0-alpha.49 → 0.0.0-alpha.50
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -6,7 +6,7 @@ import { StyleSheet } from "react-native-unistyles";
|
|
|
6
6
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
7
7
|
import { ScrollView } from "react-native-unistyles/components/native/ScrollView";
|
|
8
8
|
import { Pressable } from "react-native-unistyles/components/native/Pressable";
|
|
9
|
-
import { useMemo, useState } from "react";
|
|
9
|
+
import { useEffect, useMemo, useState } from "react";
|
|
10
10
|
//#region src/extensions/workspaces/sessions/(frontend)/tool/_lib/askUserText.ts
|
|
11
11
|
function isRecord$1(value) {
|
|
12
12
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -278,6 +278,7 @@ function QuestionnaireView(props) {
|
|
|
278
278
|
const question = questions[current];
|
|
279
279
|
const entry = draftEntry(draft, current);
|
|
280
280
|
const preview = question?.options[previewIndex]?.preview;
|
|
281
|
+
const advances = question !== void 0 && !question.multiSelect && !question.options.some((option) => option.preview);
|
|
281
282
|
return /* @__PURE__ */ jsxs(View, {
|
|
282
283
|
style: styles.frame,
|
|
283
284
|
testID: "questionnaire",
|
|
@@ -349,7 +350,7 @@ function QuestionnaireView(props) {
|
|
|
349
350
|
onPress: () => {
|
|
350
351
|
selectionTick();
|
|
351
352
|
onPreview(index);
|
|
352
|
-
onDraft(chooseOption(draft, current, option.label, question.multiSelect),
|
|
353
|
+
onDraft(chooseOption(draft, current, option.label, question.multiSelect), advances);
|
|
353
354
|
},
|
|
354
355
|
children: [/* @__PURE__ */ jsx(Text, {
|
|
355
356
|
style: [styles.mark, chosen && styles.selectedInk],
|
|
@@ -405,7 +406,7 @@ function QuestionnaireView(props) {
|
|
|
405
406
|
style: styles.meta,
|
|
406
407
|
children: "the agent asked something this cockpit could not read"
|
|
407
408
|
})
|
|
408
|
-
}), /* @__PURE__ */ jsxs(View, {
|
|
409
|
+
}, current), /* @__PURE__ */ jsxs(View, {
|
|
409
410
|
style: styles.actions,
|
|
410
411
|
children: [
|
|
411
412
|
questions.length > 1 && !isComplete(draft) ? /* @__PURE__ */ jsx(Button, {
|
|
@@ -433,11 +434,23 @@ function QuestionnaireView(props) {
|
|
|
433
434
|
})] })]
|
|
434
435
|
});
|
|
435
436
|
}
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
const
|
|
437
|
+
const savedDrafts = /* @__PURE__ */ new Map();
|
|
438
|
+
function Questionnaire({ draftKey, questions, answer, cancel }) {
|
|
439
|
+
const saved = savedDrafts.get(draftKey);
|
|
440
|
+
const [draft, setDraft] = useState(() => saved?.draft ?? emptyDraft(questions.length));
|
|
441
|
+
const [current, setCurrent] = useState(saved?.current ?? 0);
|
|
439
442
|
const [previewIndex, setPreviewIndex] = useState(0);
|
|
440
443
|
const [collapsed, setCollapsed] = useState(false);
|
|
444
|
+
useEffect(() => {
|
|
445
|
+
savedDrafts.set(draftKey, {
|
|
446
|
+
draft,
|
|
447
|
+
current
|
|
448
|
+
});
|
|
449
|
+
}, [
|
|
450
|
+
draftKey,
|
|
451
|
+
draft,
|
|
452
|
+
current
|
|
453
|
+
]);
|
|
441
454
|
const show = (index) => {
|
|
442
455
|
setCurrent(index);
|
|
443
456
|
setPreviewIndex(0);
|
|
@@ -467,11 +480,13 @@ function Questionnaire({ questions, answer, cancel }) {
|
|
|
467
480
|
/** The dialog identity owns the draft, so another session or request cannot inherit its answers. */
|
|
468
481
|
function QuestionnairePrompt({ args, sessionId, dialog, answer, cancel }) {
|
|
469
482
|
const questions = useMemo(() => readPromptQuestions(args), [args]);
|
|
483
|
+
const draftKey = `${sessionId}:${dialog.id}:${String(questions.length)}`;
|
|
470
484
|
return /* @__PURE__ */ jsx(Questionnaire, {
|
|
485
|
+
draftKey,
|
|
471
486
|
questions,
|
|
472
487
|
answer,
|
|
473
488
|
cancel
|
|
474
|
-
},
|
|
489
|
+
}, draftKey);
|
|
475
490
|
}
|
|
476
491
|
const styles = StyleSheet.create((theme) => ({
|
|
477
492
|
frame: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"native.mjs","names":["isRecord","asString","ToolMessageRenderProps","fontOf","MessageItem","MessageItemBody","MessageItemHeader","MessageItemStatus","toolTone","StyleSheet","askCallSummary","askResultView","AskUserQuestionToolMessage","props","summary","args","view","result","details","output","count","headers","running","kind","answers","length","styles","map","answer","index","String","question","text","check","prompt","isError","error","undefined","create","theme","gap","spacing","uni__dependencies","fontFamily","sans","fontSize","base","lineHeight","lineHeightRelaxed","color","colors","dim","green","cyan","red","styles","ToolPromptRenderProps","Button","ChevronDownIcon","ChevronUpIcon","fontOf","Markdown","SectionLabel","selectionTick","Textarea","useMemo","useState","StyleSheet","encodeAnswerEnvelope","chooseOption","CUSTOM_LABEL","draftAnswers","draftEntry","emptyDraft","isAnswered","isComplete","nextUnanswered","PromptQuestion","QuestionnaireDraft","readPromptQuestions","setCustom","setNotes","QuestionnaireViewProps","questions","draft","current","previewIndex","collapsed","onDraft","settles","onCurrent","index","onPreview","onCollapsed","onSubmit","onCancel","QuestionnaireView","props","question","entry","preview","options","styles","frame","header","stepBar","steps","map","step","String","expanded","body","content","meta","length","multiSelect","option","chosen","custom","selected","includes","label","description","mark","selectedInk","optionText","field","value","notes","actions","Questionnaire","answer","cancel","setDraft","setCurrent","setPreviewIndex","setCollapsed","show","next","pending","QuestionnairePrompt","args","sessionId","dialog","id","create","theme","maxHeight","flexShrink","backgroundColor","colors","rail","borderTopWidth","borderColor","uni__dependencies","flexDirection","alignItems","paddingRight","spacing","flex","gap","padding","paddingHorizontal","paddingBottom","fontFamily","sans","fontSize","base","lineHeight","lineHeightRelaxed","color","hi","sm","dim","minHeight","borderWidth","borderRadius","md","lg","magenta","fontWeight","semibold","text","paddingLeft","borderLeftWidth","flexWrap","justifyContent","toolAskUserQuestion"],"sources":["../../src/extensions/workspaces/sessions/(frontend)/tool/_lib/askUserText.ts","../../src/extensions/workspaces/sessions/(frontend)/tool/_components/AskUserQuestionToolMessage.native.tsx","../../src/types/askUserWire.ts","../../src/extensions/workspaces/sessions/(frontend)/tool/_lib/questionnaireDraft.ts","../../src/extensions/workspaces/sessions/(frontend)/tool/_components/QuestionnairePrompt.native.tsx","../../src/extensions/workspaces/sessions/(frontend)/tool/askUserQuestion.native.tsx","../../generated/native.ts"],"sourcesContent":["/**\n * Pure text shaping for the ask_user_question card, apart from React so the\n * unit suite covers it without a DOM. Args and details are wire JSON; the\n * shapes narrowed here mirror src/types/questionnaire.ts, which this browser\n * module may not import at runtime.\n */\n\ntype Args = Readonly<Record<string, unknown>>;\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction asString(value: unknown): string {\n return typeof value === 'string' ? value : '';\n}\n\nexport interface AskCallSummary {\n /** \"N question(s)\", the TUI heading's purpose. */\n count: string;\n /** The question headers, comma separated; empty when none carry one. */\n headers: string;\n}\n\n/** The call header: how many questions, and their headers in parentheses. */\nexport function askCallSummary(args: Args): AskCallSummary {\n const questions = Array.isArray(args.questions) ? args.questions.filter(isRecord) : [];\n const headers = questions.map((question) => asString(question.header)).filter(Boolean);\n return {\n count: `${questions.length} question${questions.length === 1 ? '' : 's'}`,\n headers: headers.join(', '),\n };\n}\n\nexport interface AnsweredQuestion {\n question: string;\n answer: string;\n}\n\nexport type AskResultView =\n | { kind: 'cancelled' }\n | { kind: 'voice'; prompt: string }\n | { kind: 'answers'; answers: AnsweredQuestion[] }\n | { kind: 'text'; text: string };\n\n/**\n * The result body the TUI's renderResult chooses: a cancelled glyph, the\n * voice prompt when the questionnaire was handed to voice, the answered\n * list, or the raw text when the tool attached no details (an error path).\n */\nexport function askResultView(details: unknown, output: string): AskResultView {\n if (!isRecord(details) || !Array.isArray(details.answers)) return { kind: 'text', text: output };\n if (details.cancelled === true) return { kind: 'cancelled' };\n if (details.delivery === 'voice' && typeof details.voicePrompt === 'string') {\n return { kind: 'voice', prompt: details.voicePrompt };\n }\n const answers = details.answers.filter(isRecord).map((answer) => ({\n question: asString(answer.question),\n answer:\n answer.kind === 'multi'\n ? (Array.isArray(answer.selected) ? answer.selected : []).filter((v) => typeof v === 'string').join(', ')\n : asString(answer.answer),\n }));\n return { kind: 'answers', answers };\n}\n","import type { ToolMessageRenderProps } from '@agimon-ai/po-core/native';\nimport {\n fontOf,\n MessageItem,\n MessageItemBody,\n MessageItemHeader,\n MessageItemStatus,\n toolTone,\n} from '@agimon-ai/po-native-components';\nimport { Text, View } from 'react-native';\nimport { StyleSheet } from 'react-native-unistyles';\n\nimport { askCallSummary, askResultView } from '../_lib/askUserText';\n\nexport function AskUserQuestionToolMessage(props: ToolMessageRenderProps) {\n const summary = askCallSummary(props.args);\n const view = askResultView(props.result?.details, props.output);\n return (\n <MessageItem tone={toolTone(props)}>\n <MessageItemHeader title=\"ask_user_question\">{`${summary.count}${summary.headers ? ` (${summary.headers})` : ''}`}</MessageItemHeader>\n <MessageItemBody testID=\"tool-result-ask_user_question\">\n {props.running ? (\n <MessageItemStatus tone=\"running\">running</MessageItemStatus>\n ) : view.kind === 'cancelled' ? (\n <MessageItemStatus tone=\"running\">cancelled</MessageItemStatus>\n ) : view.kind === 'answers' ? (\n view.answers.length === 0 ? (\n <MessageItemStatus tone=\"ok\">submitted</MessageItemStatus>\n ) : (\n <View style={styles.answers}>\n {view.answers.map((answer, index) => (\n <Text key={`${String(index)}:${answer.question}`} style={styles.text}>\n <Text style={styles.check}>✓ </Text>\n <Text style={styles.question}>{answer.question}</Text>: {answer.answer}\n </Text>\n ))}\n </View>\n )\n ) : view.kind === 'voice' ? (\n <Text style={styles.text}>{view.prompt}</Text>\n ) : view.text ? (\n <Text\n style={[styles.text, props.isError && styles.error]}\n accessibilityRole={props.isError ? 'alert' : undefined}\n >\n {view.text}\n </Text>\n ) : (\n <MessageItemStatus tone={props.isError ? 'error' : 'ok'}>\n {props.isError ? 'failed' : 'submitted'}\n </MessageItemStatus>\n )}\n </MessageItemBody>\n </MessageItem>\n );\n}\n\nconst styles = StyleSheet.create((theme) => ({\n answers: { gap: theme.spacing(2) },\n text: {\n fontFamily: fontOf(theme.fontFamily.sans),\n fontSize: theme.fontSize.base,\n lineHeight: theme.lineHeightRelaxed.base,\n color: theme.colors.dim,\n },\n check: { color: theme.colors.green },\n question: { color: theme.colors.cyan },\n error: { color: theme.colors.red },\n}));\n","import type { QuestionAnswer } from './questionnaire';\n\n/**\n * How a rich client answers the whole questionnaire in one reply.\n *\n * Pi's select request carries a title and a list of labels and nothing else,\n * so over RPC the questionnaire degrades into one request per question, asked\n * forwards only. A client that can already see the whole call (the cockpit\n * reads it from the tool's arguments) has no use for that: it renders every\n * question at once, lets the reader move between them, and replies once.\n *\n * The reply rides on the select response, because that is the only channel a\n * blocked extension has. An ordinary label is still an ordinary label, so a\n * client that knows nothing about this keeps the per-question flow exactly as\n * it was; only a response carrying the prefix is read as the whole set.\n *\n * It lives under src/types because that is the one server root a browser\n * bundle may read, so neither half can drift from the other.\n */\nexport const ANSWER_ENVELOPE_PREFIX = 'po-ask-user/v1:';\n\n/** A response that claims the prefix but cannot be read is refused, never guessed at. */\nexport type AnswerEnvelopeRead =\n | { readonly kind: 'absent' }\n | { readonly kind: 'malformed' }\n | { readonly kind: 'envelope'; readonly payload: unknown };\n\n/** Beyond this a response is not a questionnaire answer, whatever it claims. */\nexport const MAX_ENVELOPE_BYTES = 64 * 1024;\n\nexport function encodeAnswerEnvelope(answers: readonly QuestionAnswer[]): string {\n return `${ANSWER_ENVELOPE_PREFIX}${JSON.stringify({ answers })}`;\n}\n\nexport function decodeAnswerEnvelope(value: string | undefined): AnswerEnvelopeRead {\n if (value === undefined || !value.startsWith(ANSWER_ENVELOPE_PREFIX)) return { kind: 'absent' };\n if (value.length > MAX_ENVELOPE_BYTES) return { kind: 'malformed' };\n try {\n return { kind: 'envelope', payload: JSON.parse(value.slice(ANSWER_ENVELOPE_PREFIX.length)) };\n } catch {\n // Unreadable JSON behind the prefix: the sender meant an envelope and got\n // it wrong, which is a refusal rather than a label the model should see.\n return { kind: 'malformed' };\n }\n}\n","/**\n * The questionnaire the cockpit is filling in, as a value.\n *\n * Apart from React so the unit suite covers it without a DOM, the same way\n * askUserText.ts is. The questions come from the running tool call's\n * arguments, which are wire JSON: the shapes narrowed here mirror\n * src/schemas/questionnaire.ts, which a browser module may not import.\n *\n * Everything is held here until the reader submits, which is the whole point.\n * A step the reader goes back to still has what they chose, and the agent is\n * asked once rather than once per question.\n */\nimport type { QuestionAnswer } from '../../../../../../types/questionnaire';\n\n/** The row every question gets, so a reader always has a way past a bad set of options. */\nexport const CUSTOM_LABEL = 'Type something.';\n\nexport interface PromptOption {\n label: string;\n description: string;\n preview?: string;\n}\n\nexport interface PromptQuestion {\n question: string;\n header: string;\n multiSelect: boolean;\n options: PromptOption[];\n}\n\nexport interface QuestionDraft {\n /** Chosen option labels: at most one unless the question is multiSelect. */\n selected: string[];\n /** Text typed in place of an option; null while the reader has not typed any. */\n custom: string | null;\n notes: string;\n}\n\nexport type QuestionnaireDraft = readonly QuestionDraft[];\n\nconst EMPTY_ENTRY: QuestionDraft = { selected: [], custom: null, notes: '' };\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction asString(value: unknown): string {\n return typeof value === 'string' ? value : '';\n}\n\nfunction readOption(value: unknown): PromptOption | null {\n if (!isRecord(value)) return null;\n const label = asString(value.label);\n if (label === '') return null;\n const preview = asString(value.preview);\n return { label, description: asString(value.description), ...(preview === '' ? {} : { preview }) };\n}\n\n/** The questions as the tool was called with them; an unreadable call yields none. */\nexport function readPromptQuestions(args: Readonly<Record<string, unknown>>): PromptQuestion[] {\n if (!Array.isArray(args.questions)) return [];\n return args.questions.flatMap((entry) => {\n if (!isRecord(entry)) return [];\n const question = asString(entry.question);\n if (question === '') return [];\n const options = (Array.isArray(entry.options) ? entry.options : []).flatMap((option) => {\n const read = readOption(option);\n return read === null ? [] : [read];\n });\n return [{ question, header: asString(entry.header), multiSelect: entry.multiSelect === true, options }];\n });\n}\n\nexport function emptyDraft(count: number): QuestionnaireDraft {\n return Array.from({ length: count }, () => EMPTY_ENTRY);\n}\n\nexport function draftEntry(draft: QuestionnaireDraft, index: number): QuestionDraft {\n return draft[index] ?? EMPTY_ENTRY;\n}\n\nfunction withEntry(draft: QuestionnaireDraft, index: number, patch: Partial<QuestionDraft>): QuestionnaireDraft {\n if (index < 0 || index >= draft.length) return draft;\n return draft.map((entry, position) => (position === index ? { ...entry, ...patch } : entry));\n}\n\n/**\n * Picks an option. A single-select question replaces its choice; a multiSelect\n * one toggles. Either way the typed answer is dropped, because an option and\n * free text are two answers to the same question and only one can be sent.\n */\nexport function chooseOption(\n draft: QuestionnaireDraft,\n index: number,\n label: string,\n multiSelect: boolean,\n): QuestionnaireDraft {\n const current = draftEntry(draft, index).selected;\n const selected = multiSelect\n ? current.includes(label)\n ? current.filter((entry) => entry !== label)\n : [...current, label]\n : [label];\n return withEntry(draft, index, { selected, custom: null });\n}\n\n/** Types an answer instead of choosing one; null returns to the options. */\nexport function setCustom(draft: QuestionnaireDraft, index: number, custom: string | null): QuestionnaireDraft {\n return withEntry(draft, index, { custom, selected: [] });\n}\n\nexport function setNotes(draft: QuestionnaireDraft, index: number, notes: string): QuestionnaireDraft {\n return withEntry(draft, index, { notes });\n}\n\nexport function isAnswered(entry: QuestionDraft): boolean {\n return entry.selected.length > 0 || (entry.custom !== null && entry.custom.trim() !== '');\n}\n\n/** The next question still needing an answer, searching after `from` first; -1 when none is left. */\nexport function nextUnanswered(draft: QuestionnaireDraft, from: number): number {\n const after = draft.findIndex((entry, index) => index > from && !isAnswered(entry));\n return after >= 0 ? after : draft.findIndex((entry) => !isAnswered(entry));\n}\n\nexport function isComplete(draft: QuestionnaireDraft): boolean {\n return draft.length > 0 && draft.every(isAnswered);\n}\n\n/**\n * The answers as the tool reads them back. `preview` is left off: the session\n * half looks it up from the option it declared, so sending one here would only\n * be a claim it has to ignore.\n */\nexport function draftAnswers(questions: readonly PromptQuestion[], draft: QuestionnaireDraft): QuestionAnswer[] {\n return questions.flatMap((question, index): QuestionAnswer[] => {\n const entry = draftEntry(draft, index);\n if (!isAnswered(entry)) return [];\n const notes = entry.notes.trim();\n const base = { questionIndex: index, question: question.question, ...(notes === '' ? {} : { notes }) };\n if (entry.custom !== null && entry.custom.trim() !== '') {\n return [{ ...base, kind: 'custom' as const, answer: entry.custom }];\n }\n if (question.multiSelect) {\n return [{ ...base, kind: 'multi' as const, answer: null, selected: [...entry.selected] }];\n }\n return [{ ...base, kind: 'option' as const, answer: entry.selected[0] ?? null }];\n });\n}\n","import type { ToolPromptRenderProps } from '@agimon-ai/po-core/native';\nimport {\n Button,\n ChevronDownIcon,\n ChevronUpIcon,\n fontOf,\n Markdown,\n SectionLabel,\n selectionTick,\n Textarea,\n} from '@agimon-ai/po-native-components';\nimport { useMemo, useState } from 'react';\nimport { Pressable, ScrollView, Text, View } from 'react-native';\nimport { StyleSheet } from 'react-native-unistyles';\n\nimport { encodeAnswerEnvelope } from '../../../../../../types/askUserWire';\nimport {\n chooseOption,\n CUSTOM_LABEL,\n draftAnswers,\n draftEntry,\n emptyDraft,\n isAnswered,\n isComplete,\n nextUnanswered,\n type PromptQuestion,\n type QuestionnaireDraft,\n readPromptQuestions,\n setCustom,\n setNotes,\n} from '../_lib/questionnaireDraft';\n\nexport interface QuestionnaireViewProps {\n questions: readonly PromptQuestion[];\n draft: QuestionnaireDraft;\n current: number;\n previewIndex: number;\n /** Folded down to the step bar so the conversation above can be read; the draft stays. */\n collapsed: boolean;\n onDraft: (draft: QuestionnaireDraft, settles: boolean) => void;\n onCurrent: (index: number) => void;\n onPreview: (index: number) => void;\n onCollapsed: (collapsed: boolean) => void;\n onSubmit: () => void;\n onCancel: () => void;\n}\n\n/** Pure presentation for the approved-target stories and the native prompt host. */\nexport function QuestionnaireView(props: QuestionnaireViewProps) {\n const {\n questions,\n draft,\n current,\n previewIndex,\n collapsed,\n onDraft,\n onCurrent,\n onPreview,\n onCollapsed,\n onSubmit,\n onCancel,\n } = props;\n const question = questions[current];\n const entry = draftEntry(draft, current);\n const preview = question?.options[previewIndex]?.preview;\n return (\n <View style={styles.frame} testID=\"questionnaire\">\n <View style={styles.header}>\n <ScrollView\n horizontal\n style={styles.stepBar}\n contentContainerStyle={styles.steps}\n accessibilityLabel=\"Questions\"\n >\n {questions.map((step, index) => (\n <Button\n key={`${String(index)}:${step.question}`}\n size=\"card\"\n variant={index === current ? 'secondary' : 'ghost'}\n aria-selected={index === current}\n accessibilityLabel={`${step.header || `Question ${String(index + 1)}`}, ${isAnswered(draftEntry(draft, index)) ? 'answered' : 'not answered'}`}\n testID={`questionnaire-step-${String(index)}`}\n onPress={() => {\n selectionTick();\n onCurrent(index);\n }}\n >\n {`${isAnswered(draftEntry(draft, index)) ? '✓ ' : ''}${step.header || `Question ${String(index + 1)}`}`}\n </Button>\n ))}\n </ScrollView>\n <Button\n variant=\"ghost\"\n size=\"icon\"\n accessibilityLabel={collapsed ? 'expand' : 'collapse'}\n accessibilityState={{ expanded: !collapsed }}\n accessibilityHint={collapsed ? 'show the questions again' : 'fold away to read the conversation'}\n testID=\"questionnaire-collapse\"\n onPress={() => onCollapsed(!collapsed)}\n >\n {collapsed ? <ChevronUpIcon size={16} color=\"dim\" /> : <ChevronDownIcon size={16} color=\"dim\" />}\n </Button>\n </View>\n {collapsed ? null : (\n <>\n <ScrollView style={styles.body} contentContainerStyle={styles.content} keyboardShouldPersistTaps=\"handled\">\n {question ? (\n <>\n <Text style={styles.meta}>\n Question {current + 1} / {questions.length}\n {question.multiSelect ? ' · Choose any that fit' : ''}\n </Text>\n <Text style={styles.question} testID=\"questionnaire-question\">\n {question.question}\n </Text>\n {question.options.map((option, index) => {\n const chosen = entry.custom === null && entry.selected.includes(option.label);\n return (\n <Pressable\n key={`${String(index)}:${option.label}`}\n accessibilityRole={question.multiSelect ? 'checkbox' : 'radio'}\n aria-checked={chosen}\n accessibilityLabel={option.label}\n accessibilityHint={option.description}\n testID={`questionnaire-option-${String(index)}`}\n style={[styles.option, chosen && styles.chosen]}\n onFocus={() => onPreview(index)}\n onPress={() => {\n selectionTick();\n onPreview(index);\n onDraft(\n chooseOption(draft, current, option.label, question.multiSelect),\n !question.multiSelect,\n );\n }}\n >\n <Text style={[styles.mark, chosen && styles.selectedInk]}>\n {question.multiSelect ? (chosen ? '☑' : '☐') : chosen ? '●' : '○'}\n </Text>\n <View style={styles.optionText}>\n <Text style={styles.label}>{option.label}</Text>\n {option.description ? <Text style={styles.meta}>{option.description}</Text> : null}\n </View>\n </Pressable>\n );\n })}\n <Button\n variant={entry.custom === null ? 'ghost' : 'secondary'}\n size=\"card\"\n testID=\"questionnaire-option-custom\"\n onPress={() => onDraft(setCustom(draft, current, entry.custom ?? ''), false)}\n >\n {CUSTOM_LABEL}\n </Button>\n {entry.custom !== null ? (\n <View style={styles.field}>\n <SectionLabel>Your answer</SectionLabel>\n <Textarea\n autoFocus\n rows={2}\n accessibilityLabel=\"Your answer\"\n testID=\"questionnaire-custom\"\n value={entry.custom}\n placeholder=\"type your answer…\"\n onChangeText={(value) => onDraft(setCustom(draft, current, value), false)}\n />\n </View>\n ) : null}\n {preview && entry.custom === null ? (\n <View style={styles.preview} testID=\"questionnaire-preview\">\n <SectionLabel>Preview</SectionLabel>\n <Markdown text={preview} />\n </View>\n ) : null}\n <View style={styles.field}>\n <SectionLabel>Note (optional)</SectionLabel>\n <Textarea\n rows={1}\n accessibilityLabel=\"Note (optional)\"\n testID=\"questionnaire-notes\"\n value={entry.notes}\n placeholder=\"anything the options do not cover\"\n onChangeText={(value) => onDraft(setNotes(draft, current, value), false)}\n />\n </View>\n </>\n ) : (\n <Text style={styles.meta}>the agent asked something this cockpit could not read</Text>\n )}\n </ScrollView>\n <View style={styles.actions}>\n {questions.length > 1 && !isComplete(draft) ? (\n <Button\n size=\"card\"\n disabled={!isAnswered(entry)}\n onPress={() => onDraft(draft, true)}\n testID=\"questionnaire-next\"\n >\n Next question\n </Button>\n ) : null}\n <Button size=\"card\" onPress={onCancel} testID=\"questionnaire-cancel\">\n cancel\n </Button>\n <Button\n size=\"card\"\n variant=\"primary\"\n disabled={!isComplete(draft)}\n onPress={onSubmit}\n testID=\"questionnaire-submit\"\n >\n submit answers\n </Button>\n </View>\n </>\n )}\n </View>\n );\n}\n\nfunction Questionnaire({\n questions,\n answer,\n cancel,\n}: {\n questions: readonly PromptQuestion[];\n answer: ToolPromptRenderProps['answer'];\n cancel: ToolPromptRenderProps['cancel'];\n}) {\n const [draft, setDraft] = useState(() => emptyDraft(questions.length));\n const [current, setCurrent] = useState(0);\n const [previewIndex, setPreviewIndex] = useState(0);\n const [collapsed, setCollapsed] = useState(false);\n const show = (index: number) => {\n setCurrent(index);\n setPreviewIndex(0);\n setCollapsed(false);\n };\n return (\n <QuestionnaireView\n questions={questions}\n draft={draft}\n current={current}\n previewIndex={previewIndex}\n collapsed={collapsed}\n onDraft={(next, settles) => {\n setDraft(next);\n if (!settles) return;\n const pending = nextUnanswered(next, current);\n if (pending >= 0 && pending !== current) show(pending);\n }}\n onCurrent={show}\n onPreview={setPreviewIndex}\n onCollapsed={setCollapsed}\n onSubmit={() => {\n if (isComplete(draft)) answer(encodeAnswerEnvelope(draftAnswers(questions, draft)));\n }}\n onCancel={cancel}\n />\n );\n}\n\n/** The dialog identity owns the draft, so another session or request cannot inherit its answers. */\nexport function QuestionnairePrompt({ args, sessionId, dialog, answer, cancel }: ToolPromptRenderProps) {\n const questions = useMemo(() => readPromptQuestions(args), [args]);\n return (\n <Questionnaire\n key={`${sessionId}:${dialog.id}:${String(questions.length)}`}\n questions={questions}\n answer={answer}\n cancel={cancel}\n />\n );\n}\n\nconst styles = StyleSheet.create((theme) => ({\n frame: {\n maxHeight: '75%',\n flexShrink: 1,\n backgroundColor: theme.colors.rail,\n borderTopWidth: 1,\n borderColor: theme.colors['edge-magenta'],\n },\n header: { flexDirection: 'row', alignItems: 'center', paddingRight: theme.spacing(3) },\n stepBar: { flex: 1 },\n steps: { gap: theme.spacing(2), padding: theme.spacing(3) },\n body: { flexShrink: 1 },\n content: { gap: theme.spacing(2), paddingHorizontal: theme.spacing(3), paddingBottom: theme.spacing(3) },\n question: {\n fontFamily: fontOf(theme.fontFamily.sans),\n fontSize: theme.fontSize.base,\n lineHeight: theme.lineHeightRelaxed.base,\n color: theme.colors.hi,\n },\n meta: {\n fontFamily: fontOf(theme.fontFamily.sans),\n fontSize: theme.fontSize.sm,\n lineHeight: theme.lineHeightRelaxed.sm,\n color: theme.colors.dim,\n },\n option: {\n flexDirection: 'row',\n gap: theme.spacing(3),\n minHeight: 44,\n padding: theme.spacing(3),\n borderWidth: 1,\n borderColor: theme.colors['border-soft'],\n borderRadius: theme.borderRadius.md,\n },\n chosen: { borderColor: theme.colors['edge-magenta'], backgroundColor: theme.colors['tint-magenta'] },\n mark: { fontFamily: fontOf(theme.fontFamily.sans), fontSize: theme.fontSize.lg, color: theme.colors.dim },\n selectedInk: { color: theme.colors.magenta },\n optionText: { flex: 1, gap: theme.spacing(1) },\n label: {\n fontFamily: fontOf(theme.fontFamily.sans),\n fontSize: theme.fontSize.base,\n lineHeight: theme.lineHeightRelaxed.base,\n fontWeight: theme.fontWeight.semibold,\n color: theme.colors.text,\n },\n field: { gap: theme.spacing(2) },\n preview: {\n gap: theme.spacing(2),\n paddingLeft: theme.spacing(3),\n borderLeftWidth: 2,\n borderColor: theme.colors['edge-magenta'],\n },\n actions: {\n flexDirection: 'row',\n flexWrap: 'wrap',\n justifyContent: 'flex-end',\n gap: theme.spacing(2),\n padding: theme.spacing(3),\n borderTopWidth: 1,\n borderColor: theme.colors['border-soft'],\n },\n}));\n","import { defineToolRenderer } from '@agimon-ai/po-core/native';\n\nimport { AskUserQuestionToolMessage } from './_components/AskUserQuestionToolMessage.native';\nimport { QuestionnairePrompt } from './_components/QuestionnairePrompt.native';\nimport { readPromptQuestions } from './_lib/questionnaireDraft';\n\nexport default defineToolRenderer({\n message: AskUserQuestionToolMessage,\n prompt: {\n claims: (dialog, args) =>\n dialog.method === 'select' &&\n readPromptQuestions(args).some((question) => dialog.title.startsWith(question.question)),\n component: QuestionnairePrompt,\n },\n});\n","// Generated by @agimon-ai/po-build. Do not edit by hand.\nimport { defineNativePlugin, type WebPluginContributions } from '@agimon-ai/po-core/native';\n\nimport toolAskUserQuestion from '../src/extensions/workspaces/sessions/(frontend)/tool/askUserQuestion.native';\n\nconst via = <T>(identity: Record<string, unknown>, value: unknown): T =>\n (value === undefined ? undefined : { ...identity, ...(value as object) }) as T;\n\nexport const nativePlugin = defineNativePlugin({\n id: 'ask-user',\n session: {\n get toolRenderers(): WebPluginContributions['toolRenderers'] { return [via({ tools: ['ask_user_question'] }, toolAskUserQuestion)]; },\n },\n});\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"native.mjs","names":["isRecord","asString","ToolMessageRenderProps","fontOf","MessageItem","MessageItemBody","MessageItemHeader","MessageItemStatus","toolTone","StyleSheet","askCallSummary","askResultView","AskUserQuestionToolMessage","props","summary","args","view","result","details","output","count","headers","running","kind","answers","length","styles","map","answer","index","String","question","text","check","prompt","isError","error","undefined","create","theme","gap","spacing","uni__dependencies","fontFamily","sans","fontSize","base","lineHeight","lineHeightRelaxed","color","colors","dim","green","cyan","red","styles","ToolPromptRenderProps","Button","ChevronDownIcon","ChevronUpIcon","fontOf","Markdown","SectionLabel","selectionTick","Textarea","useEffect","useMemo","useState","StyleSheet","encodeAnswerEnvelope","chooseOption","CUSTOM_LABEL","draftAnswers","draftEntry","emptyDraft","isAnswered","isComplete","nextUnanswered","PromptQuestion","QuestionnaireDraft","readPromptQuestions","setCustom","setNotes","QuestionnaireViewProps","questions","draft","current","previewIndex","collapsed","onDraft","settles","onCurrent","index","onPreview","onCollapsed","onSubmit","onCancel","QuestionnaireView","props","question","entry","preview","options","advances","undefined","multiSelect","some","option","styles","frame","header","stepBar","steps","map","step","String","expanded","body","content","meta","length","chosen","custom","selected","includes","label","description","mark","selectedInk","optionText","field","value","notes","actions","SavedQuestionnaire","savedDrafts","Map","Questionnaire","draftKey","answer","cancel","saved","get","setDraft","setCurrent","setPreviewIndex","setCollapsed","set","show","next","pending","QuestionnairePrompt","args","sessionId","dialog","id","create","theme","maxHeight","flexShrink","backgroundColor","colors","rail","borderTopWidth","borderColor","uni__dependencies","flexDirection","alignItems","paddingRight","spacing","flex","gap","padding","paddingHorizontal","paddingBottom","fontFamily","sans","fontSize","base","lineHeight","lineHeightRelaxed","color","hi","sm","dim","minHeight","borderWidth","borderRadius","md","lg","magenta","fontWeight","semibold","text","paddingLeft","borderLeftWidth","flexWrap","justifyContent","toolAskUserQuestion"],"sources":["../../src/extensions/workspaces/sessions/(frontend)/tool/_lib/askUserText.ts","../../src/extensions/workspaces/sessions/(frontend)/tool/_components/AskUserQuestionToolMessage.native.tsx","../../src/types/askUserWire.ts","../../src/extensions/workspaces/sessions/(frontend)/tool/_lib/questionnaireDraft.ts","../../src/extensions/workspaces/sessions/(frontend)/tool/_components/QuestionnairePrompt.native.tsx","../../src/extensions/workspaces/sessions/(frontend)/tool/askUserQuestion.native.tsx","../../generated/native.ts"],"sourcesContent":["/**\n * Pure text shaping for the ask_user_question card, apart from React so the\n * unit suite covers it without a DOM. Args and details are wire JSON; the\n * shapes narrowed here mirror src/types/questionnaire.ts, which this browser\n * module may not import at runtime.\n */\n\ntype Args = Readonly<Record<string, unknown>>;\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction asString(value: unknown): string {\n return typeof value === 'string' ? value : '';\n}\n\nexport interface AskCallSummary {\n /** \"N question(s)\", the TUI heading's purpose. */\n count: string;\n /** The question headers, comma separated; empty when none carry one. */\n headers: string;\n}\n\n/** The call header: how many questions, and their headers in parentheses. */\nexport function askCallSummary(args: Args): AskCallSummary {\n const questions = Array.isArray(args.questions) ? args.questions.filter(isRecord) : [];\n const headers = questions.map((question) => asString(question.header)).filter(Boolean);\n return {\n count: `${questions.length} question${questions.length === 1 ? '' : 's'}`,\n headers: headers.join(', '),\n };\n}\n\nexport interface AnsweredQuestion {\n question: string;\n answer: string;\n}\n\nexport type AskResultView =\n | { kind: 'cancelled' }\n | { kind: 'voice'; prompt: string }\n | { kind: 'answers'; answers: AnsweredQuestion[] }\n | { kind: 'text'; text: string };\n\n/**\n * The result body the TUI's renderResult chooses: a cancelled glyph, the\n * voice prompt when the questionnaire was handed to voice, the answered\n * list, or the raw text when the tool attached no details (an error path).\n */\nexport function askResultView(details: unknown, output: string): AskResultView {\n if (!isRecord(details) || !Array.isArray(details.answers)) return { kind: 'text', text: output };\n if (details.cancelled === true) return { kind: 'cancelled' };\n if (details.delivery === 'voice' && typeof details.voicePrompt === 'string') {\n return { kind: 'voice', prompt: details.voicePrompt };\n }\n const answers = details.answers.filter(isRecord).map((answer) => ({\n question: asString(answer.question),\n answer:\n answer.kind === 'multi'\n ? (Array.isArray(answer.selected) ? answer.selected : []).filter((v) => typeof v === 'string').join(', ')\n : asString(answer.answer),\n }));\n return { kind: 'answers', answers };\n}\n","import type { ToolMessageRenderProps } from '@agimon-ai/po-core/native';\nimport {\n fontOf,\n MessageItem,\n MessageItemBody,\n MessageItemHeader,\n MessageItemStatus,\n toolTone,\n} from '@agimon-ai/po-native-components';\nimport { Text, View } from 'react-native';\nimport { StyleSheet } from 'react-native-unistyles';\n\nimport { askCallSummary, askResultView } from '../_lib/askUserText';\n\nexport function AskUserQuestionToolMessage(props: ToolMessageRenderProps) {\n const summary = askCallSummary(props.args);\n const view = askResultView(props.result?.details, props.output);\n return (\n <MessageItem tone={toolTone(props)}>\n <MessageItemHeader title=\"ask_user_question\">{`${summary.count}${summary.headers ? ` (${summary.headers})` : ''}`}</MessageItemHeader>\n <MessageItemBody testID=\"tool-result-ask_user_question\">\n {props.running ? (\n <MessageItemStatus tone=\"running\">running</MessageItemStatus>\n ) : view.kind === 'cancelled' ? (\n <MessageItemStatus tone=\"running\">cancelled</MessageItemStatus>\n ) : view.kind === 'answers' ? (\n view.answers.length === 0 ? (\n <MessageItemStatus tone=\"ok\">submitted</MessageItemStatus>\n ) : (\n <View style={styles.answers}>\n {view.answers.map((answer, index) => (\n <Text key={`${String(index)}:${answer.question}`} style={styles.text}>\n <Text style={styles.check}>✓ </Text>\n <Text style={styles.question}>{answer.question}</Text>: {answer.answer}\n </Text>\n ))}\n </View>\n )\n ) : view.kind === 'voice' ? (\n <Text style={styles.text}>{view.prompt}</Text>\n ) : view.text ? (\n <Text\n style={[styles.text, props.isError && styles.error]}\n accessibilityRole={props.isError ? 'alert' : undefined}\n >\n {view.text}\n </Text>\n ) : (\n <MessageItemStatus tone={props.isError ? 'error' : 'ok'}>\n {props.isError ? 'failed' : 'submitted'}\n </MessageItemStatus>\n )}\n </MessageItemBody>\n </MessageItem>\n );\n}\n\nconst styles = StyleSheet.create((theme) => ({\n answers: { gap: theme.spacing(2) },\n text: {\n fontFamily: fontOf(theme.fontFamily.sans),\n fontSize: theme.fontSize.base,\n lineHeight: theme.lineHeightRelaxed.base,\n color: theme.colors.dim,\n },\n check: { color: theme.colors.green },\n question: { color: theme.colors.cyan },\n error: { color: theme.colors.red },\n}));\n","import type { QuestionAnswer } from './questionnaire';\n\n/**\n * How a rich client answers the whole questionnaire in one reply.\n *\n * Pi's select request carries a title and a list of labels and nothing else,\n * so over RPC the questionnaire degrades into one request per question, asked\n * forwards only. A client that can already see the whole call (the cockpit\n * reads it from the tool's arguments) has no use for that: it renders every\n * question at once, lets the reader move between them, and replies once.\n *\n * The reply rides on the select response, because that is the only channel a\n * blocked extension has. An ordinary label is still an ordinary label, so a\n * client that knows nothing about this keeps the per-question flow exactly as\n * it was; only a response carrying the prefix is read as the whole set.\n *\n * It lives under src/types because that is the one server root a browser\n * bundle may read, so neither half can drift from the other.\n */\nexport const ANSWER_ENVELOPE_PREFIX = 'po-ask-user/v1:';\n\n/** A response that claims the prefix but cannot be read is refused, never guessed at. */\nexport type AnswerEnvelopeRead =\n | { readonly kind: 'absent' }\n | { readonly kind: 'malformed' }\n | { readonly kind: 'envelope'; readonly payload: unknown };\n\n/** Beyond this a response is not a questionnaire answer, whatever it claims. */\nexport const MAX_ENVELOPE_BYTES = 64 * 1024;\n\nexport function encodeAnswerEnvelope(answers: readonly QuestionAnswer[]): string {\n return `${ANSWER_ENVELOPE_PREFIX}${JSON.stringify({ answers })}`;\n}\n\nexport function decodeAnswerEnvelope(value: string | undefined): AnswerEnvelopeRead {\n if (value === undefined || !value.startsWith(ANSWER_ENVELOPE_PREFIX)) return { kind: 'absent' };\n if (value.length > MAX_ENVELOPE_BYTES) return { kind: 'malformed' };\n try {\n return { kind: 'envelope', payload: JSON.parse(value.slice(ANSWER_ENVELOPE_PREFIX.length)) };\n } catch {\n // Unreadable JSON behind the prefix: the sender meant an envelope and got\n // it wrong, which is a refusal rather than a label the model should see.\n return { kind: 'malformed' };\n }\n}\n","/**\n * The questionnaire the cockpit is filling in, as a value.\n *\n * Apart from React so the unit suite covers it without a DOM, the same way\n * askUserText.ts is. The questions come from the running tool call's\n * arguments, which are wire JSON: the shapes narrowed here mirror\n * src/schemas/questionnaire.ts, which a browser module may not import.\n *\n * Everything is held here until the reader submits, which is the whole point.\n * A step the reader goes back to still has what they chose, and the agent is\n * asked once rather than once per question.\n */\nimport type { QuestionAnswer } from '../../../../../../types/questionnaire';\n\n/** The row every question gets, so a reader always has a way past a bad set of options. */\nexport const CUSTOM_LABEL = 'Type something.';\n\nexport interface PromptOption {\n label: string;\n description: string;\n preview?: string;\n}\n\nexport interface PromptQuestion {\n question: string;\n header: string;\n multiSelect: boolean;\n options: PromptOption[];\n}\n\nexport interface QuestionDraft {\n /** Chosen option labels: at most one unless the question is multiSelect. */\n selected: string[];\n /** Text typed in place of an option; null while the reader has not typed any. */\n custom: string | null;\n notes: string;\n}\n\nexport type QuestionnaireDraft = readonly QuestionDraft[];\n\nconst EMPTY_ENTRY: QuestionDraft = { selected: [], custom: null, notes: '' };\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction asString(value: unknown): string {\n return typeof value === 'string' ? value : '';\n}\n\nfunction readOption(value: unknown): PromptOption | null {\n if (!isRecord(value)) return null;\n const label = asString(value.label);\n if (label === '') return null;\n const preview = asString(value.preview);\n return { label, description: asString(value.description), ...(preview === '' ? {} : { preview }) };\n}\n\n/** The questions as the tool was called with them; an unreadable call yields none. */\nexport function readPromptQuestions(args: Readonly<Record<string, unknown>>): PromptQuestion[] {\n if (!Array.isArray(args.questions)) return [];\n return args.questions.flatMap((entry) => {\n if (!isRecord(entry)) return [];\n const question = asString(entry.question);\n if (question === '') return [];\n const options = (Array.isArray(entry.options) ? entry.options : []).flatMap((option) => {\n const read = readOption(option);\n return read === null ? [] : [read];\n });\n return [{ question, header: asString(entry.header), multiSelect: entry.multiSelect === true, options }];\n });\n}\n\nexport function emptyDraft(count: number): QuestionnaireDraft {\n return Array.from({ length: count }, () => EMPTY_ENTRY);\n}\n\nexport function draftEntry(draft: QuestionnaireDraft, index: number): QuestionDraft {\n return draft[index] ?? EMPTY_ENTRY;\n}\n\nfunction withEntry(draft: QuestionnaireDraft, index: number, patch: Partial<QuestionDraft>): QuestionnaireDraft {\n if (index < 0 || index >= draft.length) return draft;\n return draft.map((entry, position) => (position === index ? { ...entry, ...patch } : entry));\n}\n\n/**\n * Picks an option. A single-select question replaces its choice; a multiSelect\n * one toggles. Either way the typed answer is dropped, because an option and\n * free text are two answers to the same question and only one can be sent.\n */\nexport function chooseOption(\n draft: QuestionnaireDraft,\n index: number,\n label: string,\n multiSelect: boolean,\n): QuestionnaireDraft {\n const current = draftEntry(draft, index).selected;\n const selected = multiSelect\n ? current.includes(label)\n ? current.filter((entry) => entry !== label)\n : [...current, label]\n : [label];\n return withEntry(draft, index, { selected, custom: null });\n}\n\n/** Types an answer instead of choosing one; null returns to the options. */\nexport function setCustom(draft: QuestionnaireDraft, index: number, custom: string | null): QuestionnaireDraft {\n return withEntry(draft, index, { custom, selected: [] });\n}\n\nexport function setNotes(draft: QuestionnaireDraft, index: number, notes: string): QuestionnaireDraft {\n return withEntry(draft, index, { notes });\n}\n\nexport function isAnswered(entry: QuestionDraft): boolean {\n return entry.selected.length > 0 || (entry.custom !== null && entry.custom.trim() !== '');\n}\n\n/** The next question still needing an answer, searching after `from` first; -1 when none is left. */\nexport function nextUnanswered(draft: QuestionnaireDraft, from: number): number {\n const after = draft.findIndex((entry, index) => index > from && !isAnswered(entry));\n return after >= 0 ? after : draft.findIndex((entry) => !isAnswered(entry));\n}\n\nexport function isComplete(draft: QuestionnaireDraft): boolean {\n return draft.length > 0 && draft.every(isAnswered);\n}\n\n/**\n * The answers as the tool reads them back. `preview` is left off: the session\n * half looks it up from the option it declared, so sending one here would only\n * be a claim it has to ignore.\n */\nexport function draftAnswers(questions: readonly PromptQuestion[], draft: QuestionnaireDraft): QuestionAnswer[] {\n return questions.flatMap((question, index): QuestionAnswer[] => {\n const entry = draftEntry(draft, index);\n if (!isAnswered(entry)) return [];\n const notes = entry.notes.trim();\n const base = { questionIndex: index, question: question.question, ...(notes === '' ? {} : { notes }) };\n if (entry.custom !== null && entry.custom.trim() !== '') {\n return [{ ...base, kind: 'custom' as const, answer: entry.custom }];\n }\n if (question.multiSelect) {\n return [{ ...base, kind: 'multi' as const, answer: null, selected: [...entry.selected] }];\n }\n return [{ ...base, kind: 'option' as const, answer: entry.selected[0] ?? null }];\n });\n}\n","import type { ToolPromptRenderProps } from '@agimon-ai/po-core/native';\nimport {\n Button,\n ChevronDownIcon,\n ChevronUpIcon,\n fontOf,\n Markdown,\n SectionLabel,\n selectionTick,\n Textarea,\n} from '@agimon-ai/po-native-components';\nimport { useEffect, useMemo, useState } from 'react';\nimport { Pressable, ScrollView, Text, View } from 'react-native';\nimport { StyleSheet } from 'react-native-unistyles';\n\nimport { encodeAnswerEnvelope } from '../../../../../../types/askUserWire';\nimport {\n chooseOption,\n CUSTOM_LABEL,\n draftAnswers,\n draftEntry,\n emptyDraft,\n isAnswered,\n isComplete,\n nextUnanswered,\n type PromptQuestion,\n type QuestionnaireDraft,\n readPromptQuestions,\n setCustom,\n setNotes,\n} from '../_lib/questionnaireDraft';\n\nexport interface QuestionnaireViewProps {\n questions: readonly PromptQuestion[];\n draft: QuestionnaireDraft;\n current: number;\n previewIndex: number;\n /** Folded down to the step bar so the conversation above can be read; the draft stays. */\n collapsed: boolean;\n onDraft: (draft: QuestionnaireDraft, settles: boolean) => void;\n onCurrent: (index: number) => void;\n onPreview: (index: number) => void;\n onCollapsed: (collapsed: boolean) => void;\n onSubmit: () => void;\n onCancel: () => void;\n}\n\n/** Pure presentation for the approved-target stories and the native prompt host. */\nexport function QuestionnaireView(props: QuestionnaireViewProps) {\n const {\n questions,\n draft,\n current,\n previewIndex,\n collapsed,\n onDraft,\n onCurrent,\n onPreview,\n onCollapsed,\n onSubmit,\n onCancel,\n } = props;\n const question = questions[current];\n const entry = draftEntry(draft, current);\n const preview = question?.options[previewIndex]?.preview;\n // Previews stay open after a pick so the reader can compare them; Next moves on.\n const advances =\n question !== undefined && !question.multiSelect && !question.options.some((option) => option.preview);\n return (\n <View style={styles.frame} testID=\"questionnaire\">\n <View style={styles.header}>\n <ScrollView\n horizontal\n style={styles.stepBar}\n contentContainerStyle={styles.steps}\n accessibilityLabel=\"Questions\"\n >\n {questions.map((step, index) => (\n <Button\n key={`${String(index)}:${step.question}`}\n size=\"card\"\n variant={index === current ? 'secondary' : 'ghost'}\n aria-selected={index === current}\n accessibilityLabel={`${step.header || `Question ${String(index + 1)}`}, ${isAnswered(draftEntry(draft, index)) ? 'answered' : 'not answered'}`}\n testID={`questionnaire-step-${String(index)}`}\n onPress={() => {\n selectionTick();\n onCurrent(index);\n }}\n >\n {`${isAnswered(draftEntry(draft, index)) ? '✓ ' : ''}${step.header || `Question ${String(index + 1)}`}`}\n </Button>\n ))}\n </ScrollView>\n <Button\n variant=\"ghost\"\n size=\"icon\"\n accessibilityLabel={collapsed ? 'expand' : 'collapse'}\n accessibilityState={{ expanded: !collapsed }}\n accessibilityHint={collapsed ? 'show the questions again' : 'fold away to read the conversation'}\n testID=\"questionnaire-collapse\"\n onPress={() => onCollapsed(!collapsed)}\n >\n {collapsed ? <ChevronUpIcon size={16} color=\"dim\" /> : <ChevronDownIcon size={16} color=\"dim\" />}\n </Button>\n </View>\n {collapsed ? null : (\n <>\n <ScrollView\n // A new step mounts at its question, not wherever the last one was scrolled to.\n key={current}\n style={styles.body}\n contentContainerStyle={styles.content}\n keyboardShouldPersistTaps=\"handled\"\n >\n {question ? (\n <>\n <Text style={styles.meta}>\n Question {current + 1} / {questions.length}\n {question.multiSelect ? ' · Choose any that fit' : ''}\n </Text>\n <Text style={styles.question} testID=\"questionnaire-question\">\n {question.question}\n </Text>\n {question.options.map((option, index) => {\n const chosen = entry.custom === null && entry.selected.includes(option.label);\n return (\n <Pressable\n key={`${String(index)}:${option.label}`}\n accessibilityRole={question.multiSelect ? 'checkbox' : 'radio'}\n aria-checked={chosen}\n accessibilityLabel={option.label}\n accessibilityHint={option.description}\n testID={`questionnaire-option-${String(index)}`}\n style={[styles.option, chosen && styles.chosen]}\n onFocus={() => onPreview(index)}\n onPress={() => {\n selectionTick();\n onPreview(index);\n onDraft(chooseOption(draft, current, option.label, question.multiSelect), advances);\n }}\n >\n <Text style={[styles.mark, chosen && styles.selectedInk]}>\n {question.multiSelect ? (chosen ? '☑' : '☐') : chosen ? '●' : '○'}\n </Text>\n <View style={styles.optionText}>\n <Text style={styles.label}>{option.label}</Text>\n {option.description ? <Text style={styles.meta}>{option.description}</Text> : null}\n </View>\n </Pressable>\n );\n })}\n <Button\n variant={entry.custom === null ? 'ghost' : 'secondary'}\n size=\"card\"\n testID=\"questionnaire-option-custom\"\n onPress={() => onDraft(setCustom(draft, current, entry.custom ?? ''), false)}\n >\n {CUSTOM_LABEL}\n </Button>\n {entry.custom !== null ? (\n <View style={styles.field}>\n <SectionLabel>Your answer</SectionLabel>\n <Textarea\n autoFocus\n rows={2}\n accessibilityLabel=\"Your answer\"\n testID=\"questionnaire-custom\"\n value={entry.custom}\n placeholder=\"type your answer…\"\n onChangeText={(value) => onDraft(setCustom(draft, current, value), false)}\n />\n </View>\n ) : null}\n {preview && entry.custom === null ? (\n <View style={styles.preview} testID=\"questionnaire-preview\">\n <SectionLabel>Preview</SectionLabel>\n <Markdown text={preview} />\n </View>\n ) : null}\n <View style={styles.field}>\n <SectionLabel>Note (optional)</SectionLabel>\n <Textarea\n rows={1}\n accessibilityLabel=\"Note (optional)\"\n testID=\"questionnaire-notes\"\n value={entry.notes}\n placeholder=\"anything the options do not cover\"\n onChangeText={(value) => onDraft(setNotes(draft, current, value), false)}\n />\n </View>\n </>\n ) : (\n <Text style={styles.meta}>the agent asked something this cockpit could not read</Text>\n )}\n </ScrollView>\n <View style={styles.actions}>\n {questions.length > 1 && !isComplete(draft) ? (\n <Button\n size=\"card\"\n disabled={!isAnswered(entry)}\n onPress={() => onDraft(draft, true)}\n testID=\"questionnaire-next\"\n >\n Next question\n </Button>\n ) : null}\n <Button size=\"card\" onPress={onCancel} testID=\"questionnaire-cancel\">\n cancel\n </Button>\n <Button\n size=\"card\"\n variant=\"primary\"\n disabled={!isComplete(draft)}\n onPress={onSubmit}\n testID=\"questionnaire-submit\"\n >\n submit answers\n </Button>\n </View>\n </>\n )}\n </View>\n );\n}\n\ninterface SavedQuestionnaire {\n draft: QuestionnaireDraft;\n current: number;\n}\n\n// ponytail: in memory only, so a draft survives leaving the session but not the app being killed, and\n// answered dialogs are never pruned (a few small objects per app run). Persist to storage if that matters.\nconst savedDrafts = new Map<string, SavedQuestionnaire>();\n\nfunction Questionnaire({\n draftKey,\n questions,\n answer,\n cancel,\n}: {\n draftKey: string;\n questions: readonly PromptQuestion[];\n answer: ToolPromptRenderProps['answer'];\n cancel: ToolPromptRenderProps['cancel'];\n}) {\n const saved = savedDrafts.get(draftKey);\n const [draft, setDraft] = useState(() => saved?.draft ?? emptyDraft(questions.length));\n const [current, setCurrent] = useState(saved?.current ?? 0);\n const [previewIndex, setPreviewIndex] = useState(0);\n const [collapsed, setCollapsed] = useState(false);\n useEffect(() => {\n savedDrafts.set(draftKey, { draft, current });\n }, [draftKey, draft, current]);\n const show = (index: number) => {\n setCurrent(index);\n setPreviewIndex(0);\n setCollapsed(false);\n };\n return (\n <QuestionnaireView\n questions={questions}\n draft={draft}\n current={current}\n previewIndex={previewIndex}\n collapsed={collapsed}\n onDraft={(next, settles) => {\n setDraft(next);\n if (!settles) return;\n const pending = nextUnanswered(next, current);\n if (pending >= 0 && pending !== current) show(pending);\n }}\n onCurrent={show}\n onPreview={setPreviewIndex}\n onCollapsed={setCollapsed}\n onSubmit={() => {\n if (isComplete(draft)) answer(encodeAnswerEnvelope(draftAnswers(questions, draft)));\n }}\n onCancel={cancel}\n />\n );\n}\n\n/** The dialog identity owns the draft, so another session or request cannot inherit its answers. */\nexport function QuestionnairePrompt({ args, sessionId, dialog, answer, cancel }: ToolPromptRenderProps) {\n const questions = useMemo(() => readPromptQuestions(args), [args]);\n const draftKey = `${sessionId}:${dialog.id}:${String(questions.length)}`;\n return <Questionnaire key={draftKey} draftKey={draftKey} questions={questions} answer={answer} cancel={cancel} />;\n}\n\nconst styles = StyleSheet.create((theme) => ({\n frame: {\n maxHeight: '75%',\n flexShrink: 1,\n backgroundColor: theme.colors.rail,\n borderTopWidth: 1,\n borderColor: theme.colors['edge-magenta'],\n },\n header: { flexDirection: 'row', alignItems: 'center', paddingRight: theme.spacing(3) },\n stepBar: { flex: 1 },\n steps: { gap: theme.spacing(2), padding: theme.spacing(3) },\n body: { flexShrink: 1 },\n content: { gap: theme.spacing(2), paddingHorizontal: theme.spacing(3), paddingBottom: theme.spacing(3) },\n question: {\n fontFamily: fontOf(theme.fontFamily.sans),\n fontSize: theme.fontSize.base,\n lineHeight: theme.lineHeightRelaxed.base,\n color: theme.colors.hi,\n },\n meta: {\n fontFamily: fontOf(theme.fontFamily.sans),\n fontSize: theme.fontSize.sm,\n lineHeight: theme.lineHeightRelaxed.sm,\n color: theme.colors.dim,\n },\n option: {\n flexDirection: 'row',\n gap: theme.spacing(3),\n minHeight: 44,\n padding: theme.spacing(3),\n borderWidth: 1,\n borderColor: theme.colors['border-soft'],\n borderRadius: theme.borderRadius.md,\n },\n chosen: { borderColor: theme.colors['edge-magenta'], backgroundColor: theme.colors['tint-magenta'] },\n mark: { fontFamily: fontOf(theme.fontFamily.sans), fontSize: theme.fontSize.lg, color: theme.colors.dim },\n selectedInk: { color: theme.colors.magenta },\n optionText: { flex: 1, gap: theme.spacing(1) },\n label: {\n fontFamily: fontOf(theme.fontFamily.sans),\n fontSize: theme.fontSize.base,\n lineHeight: theme.lineHeightRelaxed.base,\n fontWeight: theme.fontWeight.semibold,\n color: theme.colors.text,\n },\n field: { gap: theme.spacing(2) },\n preview: {\n gap: theme.spacing(2),\n paddingLeft: theme.spacing(3),\n borderLeftWidth: 2,\n borderColor: theme.colors['edge-magenta'],\n },\n actions: {\n flexDirection: 'row',\n flexWrap: 'wrap',\n justifyContent: 'flex-end',\n gap: theme.spacing(2),\n padding: theme.spacing(3),\n borderTopWidth: 1,\n borderColor: theme.colors['border-soft'],\n },\n}));\n","import { defineToolRenderer } from '@agimon-ai/po-core/native';\n\nimport { AskUserQuestionToolMessage } from './_components/AskUserQuestionToolMessage.native';\nimport { QuestionnairePrompt } from './_components/QuestionnairePrompt.native';\nimport { readPromptQuestions } from './_lib/questionnaireDraft';\n\nexport default defineToolRenderer({\n message: AskUserQuestionToolMessage,\n prompt: {\n claims: (dialog, args) =>\n dialog.method === 'select' &&\n readPromptQuestions(args).some((question) => dialog.title.startsWith(question.question)),\n component: QuestionnairePrompt,\n },\n});\n","// Generated by @agimon-ai/po-build. Do not edit by hand.\nimport { defineNativePlugin, type WebPluginContributions } from '@agimon-ai/po-core/native';\n\nimport toolAskUserQuestion from '../src/extensions/workspaces/sessions/(frontend)/tool/askUserQuestion.native';\n\nconst via = <T>(identity: Record<string, unknown>, value: unknown): T =>\n (value === undefined ? undefined : { ...identity, ...(value as object) }) as T;\n\nexport const nativePlugin = defineNativePlugin({\n id: 'ask-user',\n session: {\n get toolRenderers(): WebPluginContributions['toolRenderers'] { return [via({ tools: ['ask_user_question'] }, toolAskUserQuestion)]; },\n },\n});\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agimon-ai/po-user-feedback",
|
|
3
|
-
"version": "0.0.0-alpha.
|
|
3
|
+
"version": "0.0.0-alpha.50",
|
|
4
4
|
"description": "Structured user questions with interactive and autonomous Voice handoff for Pi agents.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai",
|
|
@@ -66,17 +66,17 @@
|
|
|
66
66
|
"access": "public"
|
|
67
67
|
},
|
|
68
68
|
"dependencies": {
|
|
69
|
-
"@agimon-ai/po-core": "0.0.0-alpha.
|
|
70
|
-
"@agimon-ai/po-minor-mode": "0.0.0-alpha.
|
|
71
|
-
"@agimon-ai/po-web-components": "0.0.0-alpha.
|
|
72
|
-
"@agimon-ai/po-web-security": "0.0.0-alpha.
|
|
69
|
+
"@agimon-ai/po-core": "0.0.0-alpha.50",
|
|
70
|
+
"@agimon-ai/po-minor-mode": "0.0.0-alpha.50",
|
|
71
|
+
"@agimon-ai/po-web-components": "0.0.0-alpha.50",
|
|
72
|
+
"@agimon-ai/po-web-security": "0.0.0-alpha.50",
|
|
73
73
|
"@deepseek-ai/cordis": "4.0.5",
|
|
74
74
|
"typebox": "1.4.2"
|
|
75
75
|
},
|
|
76
76
|
"devDependencies": {
|
|
77
|
-
"@agimon-ai/po-build": "0.0.0-alpha.
|
|
78
|
-
"@agimon-ai/po-native-components": "0.0.0-alpha.
|
|
79
|
-
"@agimon-ai/po-voice": "0.0.0-alpha.
|
|
77
|
+
"@agimon-ai/po-build": "0.0.0-alpha.50",
|
|
78
|
+
"@agimon-ai/po-native-components": "0.0.0-alpha.50",
|
|
79
|
+
"@agimon-ai/po-voice": "0.0.0-alpha.50",
|
|
80
80
|
"@earendil-works/pi-coding-agent": "1.4.0",
|
|
81
81
|
"@tanstack/react-store": "0.11.2",
|
|
82
82
|
"@tanstack/store": "0.11.2",
|
|
@@ -92,7 +92,7 @@
|
|
|
92
92
|
"vitest": "5.0.4"
|
|
93
93
|
},
|
|
94
94
|
"peerDependencies": {
|
|
95
|
-
"@agimon-ai/po-native-components": "0.0.0-alpha.
|
|
95
|
+
"@agimon-ai/po-native-components": "0.0.0-alpha.50",
|
|
96
96
|
"@earendil-works/pi-coding-agent": "1.4.0",
|
|
97
97
|
"react-native": "0.88.0",
|
|
98
98
|
"react-native-unistyles": "^3.5.3"
|