@lynn123411/dsh-ask-user-grilling 0.6.4 → 0.7.0

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.
package/lib/client.js.map CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
- "sources": ["../src/client/index.js", "../src/client/AskGrillingRow.jsx", "../src/contract.js", "../src/card.js", "../src/client/AskGrillingRow.css", "../src/client/locales.js"],
4
- "sourcesContent": ["import { AskGrillingRow } from './AskGrillingRow.jsx';\nimport css from './AskGrillingRow.css';\nimport { NS, en, zh } from './locales.js';\n\n/** \u6D4F\u89C8\u5668\u534A\u8FB9\u7684\u5305\u8EAB\u4EFD\uFF0C\u4E5F\u662F bundle \u91CC <style> \u7684\u5F52\u5C5E\u6807\u8BB0\u3002 */\nconst PACKAGE_ID = '@lynn123411/dsh-ask-user-grilling';\nconst CSS_TAG = `${PACKAGE_ID}/AskGrillingRow.css`;\n\n/**\n * \u6D4F\u89C8\u5668\u534A\u8FB9\u8981\u7684 cordis \u670D\u52A1\u3002`locale` \u88C5\u5B57\u5178\uFF0C`slots` \u6CE8\u518C\u884C\uFF1B\n * \u5C11\u58F0\u660E\u4E00\u4E2A\u4E0D\u4F1A\u5F53\u573A\u62A5\u9519\uFF0C\u63D2\u4EF6\u4F1A\u6574\u6761 entry \u53D8 failed\u3001\u4ECE\u9875\u9762\u4E0A\u6D88\u5931\uFF08\u89C1 docs/rules/client-wiring.md\uFF09\u3002\n */\nexport const inject = ['locale', 'slots'];\n\n/** \u6CE8\u5165\u672C\u63D2\u4EF6\u7684\u6837\u5F0F\u8868\uFF1A\u6309\u6807\u7B7E\u952E\u590D\u7528\u540C\u4E00\u4E2A <style>\uFF0CHMR \u91CD\u8DD1\u65F6\u66FF\u6362\u5185\u5BB9\u800C\u4E0D\u662F\u53E0\u4E00\u4EFD\u3002 */\nfunction injectStyles() {\n if (typeof document === 'undefined') return;\n let tag = document.querySelector('style[data-plugin-css=' + JSON.stringify(CSS_TAG) + ']');\n if (tag === null) {\n tag = document.createElement('style');\n tag.dataset.plugin = PACKAGE_ID;\n tag.dataset.pluginCss = CSS_TAG;\n document.head.appendChild(tag);\n }\n tag.textContent = css;\n}\n\n/**\n * \u6D4F\u89C8\u5668\u534A\u8FB9\uFF1A\u88C5\u672C\u63D2\u4EF6\u7684\u5B57\u5178\uFF0C\u7136\u540E\u6309 wire \u5DE5\u5177\u540D\u6CE8\u518C `ask_user_grilling` \u7684\u884C\u3002\n * \u6CE8\u518C\u7684\u662F\u81EA\u6709 key\uFF0C\u5B98\u65B9 `ask_user_question` \u90A3\u5F20\u5361\u4E0D\u53D7\u5F71\u54CD\u3002\n * @param {object} ctx - \u5BA2\u6237\u7AEF\u6839\u4E0A\u4E0B\u6587\u3002\n */\nexport function apply(ctx) {\n injectStyles();\n ctx.effect(() => ctx.locale.register(NS, { zh, en }), 'dsh-ask-user-grilling: dictionaries');\n ctx.slots.inject('tool.call.toolview', () => ctx.slots.register({\n name: 'tool.call.toolview',\n key: 'ask_user_grilling',\n locale: NS,\n }, AskGrillingRow));\n}\n", "import { useMemo, useState } from 'react';\nimport {\n DisclosureRow,\n IconInspectOutlineRegular,\n IconQuestionOutlineRegular,\n MarkdownText,\n StateDot,\n} from '@deepseek-ai/dsh-client-ui-primitives';\nimport { readAskCard } from '../card.js';\n\n/**\n * `ask_user_grilling` \u7684 transcript \u884C\uFF1A\u6309 wire \u5DE5\u5177\u540D\u6CE8\u518C\u8FDB `tool.call.toolview`\uFF0C\n * \u4E0E\u5B98\u65B9 `ask_user_question` \u90A3\u5F20\u5361\u5E76\u5217\uFF08\u6CE8\u518C\u81EA\u6709\u7684 key\uFF0C\u4E0D\u63A5\u7BA1\u5B98\u65B9\u90A3\u5F20\uFF09\u3002\n *\n * \u5B98\u65B9\u5361\u7684\u53D6\u6570\u6B62\u4E8E argsRaw \u7684 id/question \u4E0E\u7ED3\u679C\u7684 answers\uFF0C\u8F6E\u672B\u8865\u5145\u9898\u4E0D\u5728 argsRaw \u91CC\u3001\n * detail \u4E5F\u4E0D\u5728\u8303\u56F4\u5185\uFF0C\u4E8E\u662F\u914D\u4E0D\u4E0A\u5BF9\u5C31\u9000\u56DE\u539F\u59CB JSON\u3002\u8FD9\u91CC\u6362\u6210\u8BFB\u5361\u6A21\u578B\uFF1AargsRaw \u91CC\u6CA1\u6709\u7684\n * \u7B54\u6848\u6309\u8865\u5145\u9898\u53E6\u5217\uFF0Cdetail \u8D70 markdown\u3002\n *\n * @param {{ block: object, toolName: string, inspect?: () => void, t: Function }} props - \u69FD\u4F4D\u7ED9\u7684\u884C\u6570\u636E\u3002\n * @returns {import('react').JSX.Element} \u8FD9\u4E00\u884C\u7684\u5361\u7247\u3002\n */\nexport function AskGrillingRow({ block, toolName, inspect, t }) {\n const [expanded, setExpanded] = useState(false);\n const toggle = () => setExpanded((value) => !value);\n const settled = 'kind' in block;\n const argsRaw = (settled ? block.call?.argsRaw : block.argsRaw) ?? '';\n const resultText = settled ? firstText(block.content) : undefined;\n const model = readAskCard({\n argsRaw,\n resultText,\n errorCode: settled ? block.error?.code : undefined,\n failed: settled && block.isError === true,\n });\n const failure = settled && block.isError === true ? failureText(resultText, block.error) : undefined;\n const expandable = argsRaw !== '' || resultText !== undefined || model.questions.length > 0;\n const open = expanded && expandable;\n const status = statusLabel(model.state, t);\n\n return (\n <div className=\"dsg-root\" data-tool={toolName} data-state={model.state}>\n {status === null ? null : <span className=\"dsg-visuallyHidden\">{status}</span>}\n <DisclosureRow\n icon={leadingFor(model.state)}\n title={t('row.title')}\n open={open}\n expandable={expandable}\n onToggle={toggle}\n expandOnRowClick\n keepContentWhenOpen\n rowClassName=\"dsg-row\"\n leadingClassName=\"dsg-leading\"\n chevronClassName=\"dsg-chevron\"\n titleClassName=\"dsg-title\"\n collapsedContent={\n <>\n <span className=\"dsg-sep\" aria-hidden=\"true\" />\n <span className=\"dsg-summary\">{summaryFor(model, t)}</span>\n </>\n }\n >\n <div className=\"dsg-bodyWrap\">\n <CardBody model={model} argsRaw={argsRaw} resultText={resultText} failure={failure} t={t} />\n {inspect === undefined ? null : (\n <button type=\"button\" className=\"dsg-inspectButton\" onClick={inspect}>\n <IconInspectOutlineRegular />\n {t('row.inspect')}\n </button>\n )}\n </div>\n </DisclosureRow>\n </div>\n );\n}\n\n/**\n * \u7ED3\u679C\u91CC\u7684\u7B2C\u4E00\u6BB5\u6587\u672C\u3002\u672C\u5DE5\u5177\u53EA\u6E32\u67D3\u4E00\u6BB5\uFF0C\u5F62\u72B6\u4E0D\u5BF9\u5C31\u5F53\u6CA1\u6709\u7ED3\u679C\u3002\n * @param {unknown} content - \u7ED3\u679C content \u6570\u7EC4\u3002\n * @returns {string|undefined} \u6587\u672C\u3002\n */\nfunction firstText(content) {\n if (!Array.isArray(content)) return undefined;\n const text = content.find((block) => block?.type === 'text');\n return typeof text?.text === 'string' ? text.text : undefined;\n}\n\n/**\n * \u5931\u8D25\u884C\u8981\u663E\u793A\u7684\u6587\u5B57\uFF1A\u7ED3\u679C\u91CC\u90A3\u6BB5\u6587\u672C\u4F18\u5148\uFF0C\u6CA1\u6709\u5C31\u7528\u9519\u8BEF\u7684 name/code \u62FC\u4E00\u884C\u3002\n * @param {string|undefined} resultText - \u7ED3\u679C\u6587\u672C\u3002\n * @param {{name?: string, code?: string}|undefined} error - \u7ED3\u679C\u8282\u70B9\u4E0A\u7684\u9519\u8BEF\u3002\n * @returns {string|undefined} \u5931\u8D25\u8BF4\u660E\u3002\n */\nfunction failureText(resultText, error) {\n if (resultText !== undefined && resultText !== '') return resultText;\n const parts = [error?.name, error?.code].filter((part) => typeof part === 'string' && part !== '');\n return parts.length === 0 ? undefined : parts.join(': ');\n}\n\n/**\n * \u884C\u7684\u524D\u5BFC\u56FE\u6807\uFF1A\u5931\u8D25\u4E0E\u62D2\u7EDD\u7528\u72B6\u6001\u70B9\uFF0C\u5176\u4F59\u7528\u95EE\u53F7\u56FE\u6807\uFF08\u4E0E\u5B98\u65B9\u884C\u540C\u4E00\u5957\u8BED\u4E49\uFF09\u3002\n * @param {string} state - \u5361\u7247\u72B6\u6001\u3002\n * @returns {import('react').ReactNode} \u524D\u5BFC\u8282\u70B9\u3002\n */\nfunction leadingFor(state) {\n if (state === 'error' || state === 'rejected') return <StateDot state=\"error\" />;\n if (state === 'cancelled' || state === 'interrupted') return <StateDot state=\"warning\" />;\n return <IconQuestionOutlineRegular />;\n}\n\n/**\n * \u5C4F\u5E55\u9605\u8BFB\u5668\u7528\u7684\u72B6\u6001\u6587\u5B57\u3002\u626B\u63CF\u5149\u4E0E\u72B6\u6001\u70B9\u90FD\u662F\u7EAF\u89C6\u89C9\u7684\u3002\n * @param {string} state - \u5361\u7247\u72B6\u6001\u3002\n * @param {Function} t - \u672C\u63D2\u4EF6\u7684\u53D6\u8BCD\u51FD\u6570\u3002\n * @returns {string|null} \u72B6\u6001\u6587\u5B57\uFF0C\u6B63\u5E38\u6001\u4E3A null\u3002\n */\nfunction statusLabel(state, t) {\n switch (state) {\n case 'running':\n return t('row.waiting');\n case 'error':\n return t('row.failed');\n case 'cancelled':\n return t('row.cancelled');\n case 'interrupted':\n return t('row.interrupted');\n default:\n return null;\n }\n}\n\n/**\n * \u6298\u53E0\u884C\u53F3\u4FA7\u7684\u6458\u8981\u3002\n * @param {object} model - \u5361\u7247\u6A21\u578B\u3002\n * @param {Function} t - \u672C\u63D2\u4EF6\u7684\u53D6\u8BCD\u51FD\u6570\u3002\n * @returns {string} \u6458\u8981\u6587\u5B57\u3002\n */\nfunction summaryFor(model, t) {\n switch (model.state) {\n case 'running':\n return t('row.waiting');\n case 'answered':\n return t('row.answered', { answered: model.answered, total: model.total });\n case 'rejected':\n return t('row.rejected');\n case 'cancelled':\n return t('row.cancelled');\n case 'interrupted':\n return t('row.interrupted');\n default:\n return t('row.failed');\n }\n}\n\n/**\n * \u6B63\u6587\uFF1A\u62D2\u7EDD\u65F6\u5148\u7ED9\u8FDD\u89C4\u6E05\u5355\uFF0C\u53D6\u6D88/\u4E2D\u65AD\u65F6\u7ED9\u8BF4\u660E\u52A0\u9898\u76EE\uFF0C\u5931\u8D25\u7ED9\u5931\u8D25\u8BF4\u660E\uFF0C\u5176\u4F59\u6309\u95EE\u7B54\u5217\u8868\u753B\u3002\n * @param {{ model: object, argsRaw: string, resultText: string|undefined, failure: string|undefined, t: Function }} props - \u5361\u7247\u6A21\u578B\u4E0E\u539F\u59CB\u6587\u672C\u3002\n * @returns {import('react').ReactNode} \u6B63\u6587\u3002\n */\nfunction CardBody({ model, argsRaw, resultText, failure, t }) {\n if (model.state === 'rejected') {\n return (\n <div className=\"dsg-card\" role=\"group\" aria-label={t('row.violations')}>\n <p className=\"dsg-verdict\">{model.error ?? t('row.rejected')}</p>\n <ul className=\"dsg-violations\">\n {model.violations.map((violation, index) => (\n <li key={String(index)}>{violation}</li>\n ))}\n </ul>\n <QuestionEntries questions={model.questions} showAnswers={false} t={t} />\n </div>\n );\n }\n if (model.questions.length === 0) {\n // \u8FD0\u884C\u4E2D\u7684\u534A\u622A JSON\u3001\u6216\u4E0D\u662F\u672C\u63D2\u4EF6\u5F62\u72B6\u7684\u7ED3\u679C\uFF1A\u539F\u6837\u7ED9\u51FA\uFF0C\u522B\u88C5\u4F5C\u8BFB\u61C2\u4E86\n const raw = model.state === 'running' ? argsRaw : resultText;\n return raw === undefined || raw === '' ? null : <pre className=\"dsg-raw\">{raw}</pre>;\n }\n if (model.state === 'cancelled' || model.state === 'interrupted') {\n return (\n <div className=\"dsg-card\">\n <p className=\"dsg-verdict\">\n {model.state === 'cancelled' ? t('row.cancelledDetail') : t('row.interruptedDetail')}\n </p>\n <QuestionEntries questions={model.questions} showAnswers={false} t={t} />\n </div>\n );\n }\n if (model.state === 'error') {\n return (\n <div className=\"dsg-card\">\n {failure === undefined ? null : <p className=\"dsg-failure\">{failure}</p>}\n <QuestionEntries questions={model.questions} showAnswers={false} t={t} />\n </div>\n );\n }\n return (\n <div className=\"dsg-card\">\n <QuestionEntries questions={model.questions} showAnswers={model.state === 'answered'} t={t} />\n </div>\n );\n}\n\n/**\n * \u9898\u76EE\u5217\u8868\uFF1A\u6709\u7B54\u6848\u7684\u753B dt/dd\uFF0C\u6CA1\u7B54\u6848\u7684\u53EA\u753B\u9898\u5E72\uFF1Bdetail \u8D70 markdown\u3002\n * @param {{ questions: object[], showAnswers: boolean, t: Function }} props - \u9898\u76EE\u4E0E\u662F\u5426\u753B\u7B54\u6848\u3002\n * @returns {import('react').JSX.Element} \u5217\u8868\u3002\n */\nfunction QuestionEntries({ questions, showAnswers, t }) {\n const labels = useMemo(() => markdownLabels(t), [t]);\n return (\n <dl className=\"dsg-list\">\n {questions.map((question) => (\n <div className=\"dsg-item\" key={question.id}>\n <dt className=\"dsg-question\">\n {question.header === '' ? null : <span className=\"dsg-header\">{question.header}</span>}\n {question.question}\n </dt>\n {question.detail === undefined ? null : (\n <div className=\"dsg-detail\">\n <MarkdownText text={question.detail} labels={labels} />\n </div>\n )}\n {showAnswers ? <AnswerLine question={question} t={t} /> : null}\n </div>\n ))}\n </dl>\n );\n}\n\n/**\n * \u4E00\u9053\u9898\u7684\u7B54\u6848\u884C\uFF1A\u9009\u4E2D\u9879\u9010\u884C\u5217\u51FA\uFF0C\u81EA\u7531\u6587\u672C\u63A5\u5728\u540E\u9762\uFF0C\u4E00\u6761\u90FD\u6CA1\u6709\u5C31\u662F\u300C\u672A\u56DE\u7B54\u300D\u3002\n * @param {{ question: object, t: Function }} props - \u9898\u76EE\u4E0E\u53D6\u8BCD\u51FD\u6570\u3002\n * @returns {import('react').JSX.Element} \u7B54\u6848\u884C\u3002\n */\nfunction AnswerLine({ question, t }) {\n return (\n <dd className=\"dsg-answer\">\n {question.answers.length === 0 ? (\n <span className=\"dsg-skipped\">{t('row.skipped')}</span>\n ) : (\n question.answers.map((answer, index) => (\n <span className=\"dsg-answerLine\" key={`${question.id}-${String(index)}`}>\n {answer}\n </span>\n ))\n )}\n </dd>\n );\n}\n\n/**\n * `MarkdownText` \u8981\u7684\u90A3\u4EFD chrome \u6807\u7B7E\uFF0C\u53D6\u81EA\u672C\u63D2\u4EF6\u7684\u5B57\u5178\u3002\n * @param {Function} t - \u672C\u63D2\u4EF6\u7684\u53D6\u8BCD\u51FD\u6570\u3002\n * @returns {{ code: { copyLabel: string, copiedLabel: string }, footnotes: string }} \u6807\u7B7E\u3002\n */\nfunction markdownLabels(t) {\n return {\n code: { copyLabel: t('copy'), copiedLabel: t('copied') },\n footnotes: t('markdown.footnotes'),\n };\n}\n", "// \u5BBF\u4E3B\u534A\u8FB9\u4E0E\u754C\u9762\u534A\u8FB9\u5171\u7528\u7684\u7EAF\u5951\u7EA6\uFF1A\u9898\u76EE\u5B57\u6BB5\u7684\u62FC\u6CD5\uFF0C\u52A0\u8F6E\u672B\u8865\u5145\u9898\u8FD9\u4E00\u9053\u7531\u4EE3\u7801\u8FFD\u52A0\u7684\u9898\u3002\n//\n// \u4E24\u5904\u5FC5\u987B\u4E00\u81F4\uFF0C\u6240\u4EE5\u53EA\u6709\u8FD9\u4E00\u4EFD\uFF1A\u8868\u5355\u9875\u6807\u9898\u4E0E transcript \u5361\u7247\u6807\u9898\u90FD\u6309\n// mergeNumberIntoHeader \u62FC\uFF1B\u754C\u9762\u6309 id \u8BA4\u51FA\u8F6E\u672B\u8865\u5145\u9898\uFF0C\u4E5F\u6309 ROUND_END_QUESTION \u62FF\u5B83\u7684\u9898\u5E72\u3002\n// \u5404\u5199\u4E00\u4EFD\u5FC5\u7136\u6F02\u3002\n\n/** \u8F6E\u672B\u8865\u5145\u9898\u7684 id \u524D\u7F00\uFF0C\u540C\u65F6\u662F\u6A21\u578B\u4E0D\u5F97\u5360\u7528\u7684\u4FDD\u7559\u524D\u7F00\u3002 */\nexport const RESERVED_ID_PREFIX = '__grill_';\n\n/**\n * \u6BCF\u6B21\u8C03\u7528\u7531\u4EE3\u7801\u8FFD\u52A0\u5728\u672B\u5C3E\u7684\u8865\u5145\u9898\u3002\u754C\u9762\u534A\u8FB9\u6309 id \u8BA4\u51FA\u5B83\uFF0C\u5BBF\u4E3B\u534A\u8FB9\u628A\u5B83\u9001\u8FDB seam\u3002\n * \u9009\u9879\u6570\u7EC4\u7684\u6D45\u62F7\u8D1D\u7531\u5BBF\u4E3B\u5728\u6295\u9012\u524D\u505A\uFF08seam \u4FA7\u4E0D\u518D\u6539\u5B83\uFF0C\u4F46\u522B\u628A\u8FD9\u4EFD\u5E38\u91CF\u4EA4\u51FA\u53BB\uFF09\u3002\n */\nexport const ROUND_END_QUESTION = Object.freeze({\n id: `${RESERVED_ID_PREFIX}round_supplement__`,\n question: '\u8FD9\u8F6E\u8FD8\u6709\u4EC0\u4E48\u8981\u8865\u5145\u6216\u8C03\u6574\u7684\u5417\uFF1F',\n header: '\u8F6E\u672B\u8865\u5145',\n options: Object.freeze([Object.freeze({ label: '\u65E0\u9700\u8865\u5145' })]),\n});\n\n/**\n * \u53BB\u6389\u9996\u5C3E\u7A7A\u767D\u540E\u662F\u5426\u4E3A\u7A7A\u3002\u7A7A\u4E32\u4E0E\u7EAF\u7A7A\u767D\u90FD\u4E0D\u503C\u5F97\u9001\u8FDB\u8868\u5355\u3002\n * @param {unknown} value - \u5F85\u5224\u5B9A\u7684\u503C\u3002\n * @returns {boolean} \u975E\u5B57\u7B26\u4E32\u6216\u53BB\u7A7A\u767D\u540E\u4E3A\u7A7A\u65F6\u4E3A true\u3002\n */\nexport function isBlank(value) {\n return typeof value !== 'string' || value.trim() === '';\n}\n\n/**\n * \u628A\u9898\u53F7\u5E76\u8FDB\u6807\u9898\uFF1A\u4E24\u8005\u90FD\u7ED9\u5199\u4F5C `<number> \u00B7 <header>`\uFF0C\u53EA\u7ED9\u4E00\u4E2A\u5C31\u662F\u90A3\u4E00\u4E2A\uFF1B\n * \u6807\u9898\u5DF2\u7ECF\u4EE5\u8BE5\u9898\u53F7\u5F00\u5934\u65F6\u539F\u6837\u9001\u51FA\u53BB\uFF08\u6A21\u578B\u5E38\u81EA\u5DF1\u5199\u6210 \"Q2 \u00B7 Deadline\"\uFF09\u3002\n * @param {string} [number] - \u9898\u53F7\u3002\n * @param {string} [header] - \u6807\u9898\u3002\n * @returns {string} \u4EA4\u7ED9\u754C\u9762\u7684\u6807\u9898\uFF1B\u4E24\u8005\u90FD\u7F3A\u65F6\u4E3A\u7A7A\u4E32\uFF08\u8C03\u7528\u65B9\u636E\u6B64\u7701\u7565\u8BE5\u5B57\u6BB5\uFF09\u3002\n */\nexport function mergeNumberIntoHeader(number, header) {\n const num = isBlank(number) ? undefined : number;\n const head = isBlank(header) ? undefined : header;\n if (num === undefined) return head ?? '';\n if (head === undefined) return num;\n return head.startsWith(num) ? head : `${num} \u00B7 ${head}`;\n}\n", "// \u4E00\u6B21 ask_user_grilling \u8C03\u7528\u7684\u5361\u7247\u6A21\u578B\uFF1A\u628A\u7EBF\u4E0A\u90A3\u4E24\u4EFD JSON\uFF08\u8C03\u7528\u7684 argsRaw\u3001\u7ED3\u679C\u7684\u6587\u672C\uFF09\n// \u7FFB\u6210\u754C\u9762\u76F4\u63A5\u80FD\u753B\u7684\u7ED3\u6784\u3002\u7EAF\u51FD\u6570\uFF1A\u4E0D\u78B0 React\u3001\u4E0D\u8BFB\u65F6\u949F\u3001\u4E0D\u8BFB\u4F1A\u8BDD\uFF0C\u5B9E\u65F6\u6E32\u67D3\u4E0E\u65E5\u5FD7\u91CD\u653E\u540C\u4E00\u6761\u8DEF\u5F84\u3002\n//\n// \u4E0E\u5B98\u65B9\u5361\uFF08@deepseek-ai/dsh-client-ui-tool \u7684 AskQuestionRow\uFF09\u7684\u53D6\u6570\u5DEE\u522B\u6709\u4E24\u5904\uFF0C\u90FD\u662F\u672C\u63D2\u4EF6\n// \u81EA\u5DF1\u9020\u6210\u7684\uFF1A\u8F6E\u672B\u8865\u5145\u9898\u662F\u5BBF\u4E3B\u8FFD\u52A0\u7684\uFF0C\u4E0D\u5728 argsRaw \u91CC\uFF1Bdetail \u4E5F\u4E0D\u5728\u5B98\u65B9\u5361\u7684\u53D6\u6570\u8303\u56F4\u5185\u3002\n// \u6240\u4EE5\u8FD9\u91CC\u6309 id \u914D\u5BF9\u4E4B\u540E\uFF0C\u628A argsRaw \u91CC\u6CA1\u6709\u7684\u7B54\u6848\u6309\u8F6E\u672B\u8865\u5145\u9898\u53E6\u884C\u5217\u51FA\uFF0C\u5E76\u628A detail \u4E00\u8D77\u5E26\u51FA\u53BB\u3002\n\nimport { ROUND_END_QUESTION, isBlank, mergeNumberIntoHeader } from './contract.js';\n\n/** \u7528\u6237\u53D6\u6D88 / \u4E2D\u65AD\u65F6\u5BBF\u4E3B\u5DE5\u5177\u6536\u5230\u7684\u9519\u8BEF\u7801\uFF0C\u754C\u9762\u636E\u6B64\u7ED9\u51FA\u672A\u4F5C\u7B54\u7684\u8BF4\u660E\u3002 */\nconst CANCELLED_CODE = 'ASK_CANCELLED';\nconst INTERRUPTED_CODE = 'ASK_ABORTED';\n\nfunction isRecord(value) {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\n/**\n * JSON.parse\uFF0C\u5931\u8D25\u8FD4\u56DE undefined\u3002running \u65F6 argsRaw \u662F\u534A\u622A JSON\uFF0C\u89E3\u6790\u5931\u8D25\u662F\u6B63\u5E38\u6001\u3002\n * @param {unknown} text - \u539F\u59CB\u6587\u672C\u3002\n * @returns {unknown} \u89E3\u6790\u7ED3\u679C\uFF0C\u6216 undefined\u3002\n */\nfunction parseJson(text) {\n if (typeof text !== 'string' || text === '') return undefined;\n try {\n return JSON.parse(text);\n } catch {\n return undefined;\n }\n}\n\nfunction stringList(value) {\n return Array.isArray(value) ? value.filter((item) => typeof item === 'string') : [];\n}\n\n/**\n * argsRaw \u2192 \u6A21\u578B\u51FA\u7684\u9898\u3002\u89E3\u6790\u5931\u8D25\u3001\u6CA1\u6709 questions\u3001\u6216 id \u91CD\u590D\uFF08\u754C\u9762\u6309 id \u914D\u5BF9\uFF0C\u91CD\u590D\u5373\u65E0\u4ECE\u914D\u5BF9\uFF09\u65F6\u8FD4\u56DE\u7A7A\u6570\u7EC4\u3002\n * @param {unknown} argsRaw - \u8C03\u7528\u7684\u539F\u59CB\u53C2\u6570\u6587\u672C\u3002\n * @returns {object[]} \u9898\u76EE\u6761\u76EE\u3002\n */\nfunction readQuestions(argsRaw) {\n const parsed = parseJson(argsRaw);\n if (!isRecord(parsed) || !Array.isArray(parsed.questions)) return [];\n const questions = [];\n const ids = new Set();\n for (const question of parsed.questions) {\n if (!isRecord(question) || typeof question.id !== 'string' || ids.has(question.id)) return [];\n ids.add(question.id);\n questions.push({\n id: question.id,\n header: mergeNumberIntoHeader(question.number, question.header),\n question: typeof question.question === 'string' ? question.question : '',\n detail: isBlank(question.detail) ? undefined : question.detail,\n answers: [],\n supplement: false,\n });\n }\n return questions;\n}\n\n/**\n * \u7ED3\u679C\u6587\u672C \u2192 \u7B54\u6848\u8BB0\u5F55\u3002\u5F62\u72B6\u4E0D\u8BA4\u8BC6\u65F6\u8FD4\u56DE undefined\uFF08\u8C03\u7528\u65B9\u6309\u8C03\u7528\u5931\u8D25\u5904\u7406\uFF09\u3002\n * @param {unknown} resultText - \u5DE5\u5177\u7ED3\u679C\u91CC\u7684\u6587\u672C\u3002\n * @returns {{rejected: boolean, violations: string[], error: string|undefined, answers: object[]}|undefined}\n */\nfunction readResult(resultText) {\n const parsed = parseJson(resultText);\n if (!isRecord(parsed)) return undefined;\n if (parsed.rejected === true) {\n return {\n rejected: true,\n violations: stringList(parsed.violations),\n error: typeof parsed.error === 'string' ? parsed.error : undefined,\n answers: [],\n };\n }\n if (!Array.isArray(parsed.answers)) return undefined;\n const answers = [];\n for (const entry of parsed.answers) {\n if (!isRecord(entry) || typeof entry.id !== 'string') return undefined;\n answers.push({\n id: entry.id,\n selected: stringList(entry.selected),\n custom: typeof entry.custom === 'string' ? entry.custom : undefined,\n });\n }\n return { rejected: false, violations: [], error: undefined, answers };\n}\n\n/**\n * \u8BFB\u51FA\u4E00\u5F20\u5361\u7684\u6A21\u578B\u3002\n * @param {{ argsRaw?: unknown, resultText?: unknown, errorCode?: unknown, failed?: boolean }} input\n * argsRaw\uFF1A\u8C03\u7528\u7684\u539F\u59CB\u53C2\u6570\uFF1BresultText\uFF1A\u7ED3\u679C\u6587\u672C\uFF08running \u65F6\u4E3A\u7A7A\uFF09\uFF1BerrorCode\uFF1A\u5931\u8D25\u7684 code\uFF1B\n * failed\uFF1A\u8FD9\u6B21\u8C03\u7528\u662F\u5426\u4EE5\u5931\u8D25\u6536\u573A\uFF08\u7ED3\u679C\u65E2\u6CA1\u6709\u7B54\u6848\u4E5F\u4E0D\u662F\u62D2\u7EDD\u65F6\u7528\u6765\u533A\u5206\u300C\u8FD0\u884C\u4E2D\u300D\u4E0E\u300C\u5931\u8D25\u300D\uFF09\u3002\n * @returns {{\n * state: 'running'|'answered'|'rejected'|'cancelled'|'interrupted'|'error',\n * questions: object[], answered: number, total: number,\n * violations: string[], error: string|undefined,\n * }} \u5361\u7247\u6A21\u578B\uFF1Bquestions \u4E3A\u7A7A\u8868\u793A\u53C2\u6570\u8FD8\u89E3\u6790\u4E0D\u51FA\u6765\uFF08\u8FD0\u884C\u4E2D\u6216\u4E0D\u662F\u672C\u63D2\u4EF6\u7684\u5F62\u72B6\uFF09\u3002\n */\nexport function readAskCard({ argsRaw, resultText, errorCode, failed = false }) {\n const questions = readQuestions(argsRaw);\n const known = new Set(questions.map((question) => question.id));\n const result = readResult(resultText);\n\n let state;\n if (errorCode === CANCELLED_CODE) state = 'cancelled';\n else if (errorCode === INTERRUPTED_CODE) state = 'interrupted';\n else if (result !== undefined) state = result.rejected ? 'rejected' : 'answered';\n else if (failed) state = 'error';\n else if (typeof resultText === 'string' && resultText !== '') state = 'error';\n else state = 'running';\n\n if (state === 'answered') {\n for (const answer of result.answers) {\n const lines = [\n ...answer.selected,\n ...answer.custom === undefined || answer.custom === '' ? [] : [answer.custom],\n ];\n const entry = questions.find((question) => question.id === answer.id);\n if (entry !== undefined) {\n entry.answers = lines;\n continue;\n }\n const supplement = answer.id === ROUND_END_QUESTION.id;\n questions.push({\n id: answer.id,\n header: supplement ? ROUND_END_QUESTION.header : '',\n question: supplement ? ROUND_END_QUESTION.question : answer.id,\n detail: undefined,\n answers: lines,\n supplement: true,\n });\n }\n } else if ((state === 'running' || state === 'cancelled' || state === 'interrupted') && questions.length > 0) {\n // \u8FD9\u4E00\u8F6E\u771F\u7684\u8FDB\u8FC7\u8868\u5355\uFF08\u6B63\u5728\u56DE\u7B54 / \u88AB\u53D6\u6D88 / \u88AB\u4E2D\u65AD\uFF09\uFF1A\u8865\u5145\u9898\u4E5F\u5728\u8868\u5355\u91CC\uFF0C\u6309\u5E38\u91CF\u8865\u4E00\u6761\u51FA\u6765\u3002\n // \u88AB\u62D2\u4E0E\u8C03\u7528\u5931\u8D25\u90FD\u6CA1\u8D70\u5230\u8868\u5355\uFF0C\u8865\u4E0A\u53BB\u7B49\u4E8E\u62A5\u544A\u4E00\u9053\u6CA1\u95EE\u8FC7\u7684\u9898\uFF1B\u53C2\u6570\u8FD8\u89E3\u6790\u4E0D\u51FA\u6765\u65F6\u540C\u7406\u2014\u2014\n // \u90A3\u662F argsRaw \u8FD8\u5728\u6D41\u5F0F\u4F20\u8F93\uFF0C\u754C\u9762\u9000\u56DE\u539F\u59CB\u6587\u672C\uFF0C\u4E0D\u5047\u88C5\u8BFB\u61C2\u4E86\u8FD9\u4E00\u8F6E\u3002\n if (!known.has(ROUND_END_QUESTION.id)) {\n questions.push({\n id: ROUND_END_QUESTION.id,\n header: ROUND_END_QUESTION.header,\n question: ROUND_END_QUESTION.question,\n detail: undefined,\n answers: [],\n supplement: true,\n });\n }\n }\n\n return finish(state, questions, result);\n}\n\n/**\n * \u6536\u53E3\uFF1A\u7B97\u5DF2\u7B54\u6570\u5E76\u5E26\u4E0A\u62D2\u7EDD\u4FE1\u606F\u3002\n * @param {string} state - \u5361\u7247\u72B6\u6001\u3002\n * @param {object[]} questions - \u6761\u76EE\u5217\u8868\u3002\n * @param {object|undefined} result - \u89E3\u6790\u51FA\u7684\u7ED3\u679C\u8BB0\u5F55\u3002\n * @returns {object} \u5361\u7247\u6A21\u578B\u3002\n */\nfunction finish(state, questions, result) {\n return {\n state,\n questions,\n answered: questions.filter((question) => question.answers.length > 0).length,\n total: questions.length,\n violations: result === undefined ? [] : result.violations,\n error: result === undefined ? undefined : result.error,\n };\n}\n", "export default \"/* ask_user_grilling \u7684 transcript \u5361\u7247\u3002\\n *\\n * \u4E0E\u5B98\u65B9 @deepseek-ai/dsh-client-ui-tool \u7684 ToolRow / AskQuestionCard \u540C\u4E00\u5957 token \u4E0E\u6570\u503C\uFF1A\\n * \u8FD9\u91CC\u53EA\u662F\u628A\u7C7B\u540D\u6362\u6210 dsg- \u524D\u7F00\uFF08\u8DE8\u63D2\u4EF6\u503C\u5BFC\u5165\u88AB\u5BA2\u6237\u7AEF bundle \u7684\u7EAF\u51C0\u5EA6\u95E8\u7981\u6321\u4F4F\uFF0C\u6240\u4EE5\u884C\u5916\u58F3\\n * \u53EA\u80FD\u81EA\u5DF1\u62FC\uFF09\uFF0C\u5F62\u72B6\u3001\u5B57\u53F7\u3001\u884C\u9AD8\u30010.5px \u53D1\u4E1D\u7EBF\u90FD\u7167\u6284\uFF0C\u907F\u514D\u540C\u4E00\u9875\u51FA\u73B0\u4E24\u5957\u89C6\u89C9\u3002 */\\n\\n.dsg-root {\\n display: flex;\\n flex-direction: column;\\n}\\n\\n.dsg-row {\\n position: relative;\\n overflow: hidden;\\n}\\n\\n/* \u8FD0\u884C\u4E2D\uFF1A\u4E0E\u5B98\u65B9\u884C\u540C\u4E00\u6761\u626B\u5149\u3002 */\\n.dsg-root[data-state='running'] .dsg-row::after {\\n content: '';\\n position: absolute;\\n top: 0;\\n bottom: 0;\\n left: 0;\\n width: 300px;\\n pointer-events: none;\\n background: linear-gradient(\\n 90deg,\\n transparent 0%,\\n color-mix(in srgb, var(--dsw-alias-bg-base) 60%, transparent) 55%,\\n transparent 100%\\n );\\n animation: 2.6s ease-out infinite dsg-tool-row-sweep;\\n}\\n\\n@keyframes dsg-tool-row-sweep {\\n 0% {\\n left: -300px;\\n }\\n 90%,\\n 100% {\\n left: 100%;\\n }\\n}\\n\\n.dsg-leading {\\n flex-shrink: 0;\\n}\\n\\n.dsg-chevron {\\n color: var(--dsw-alias-label-secondary);\\n}\\n\\n.dsg-title {\\n font-weight: 400;\\n}\\n\\n.dsg-sep {\\n flex: none;\\n width: 2px;\\n height: 2px;\\n margin: 0 8px;\\n border-radius: 1px;\\n background: var(--dsw-alias-label-caption);\\n}\\n\\n.dsg-summary {\\n flex: auto;\\n min-width: 0;\\n overflow: hidden;\\n text-overflow: ellipsis;\\n white-space: nowrap;\\n font-size: var(--dsh-content-font-size-secondary, 13px);\\n line-height: calc(24px + var(--dsh-content-font-delta, 0px));\\n color: var(--dsw-alias-label-tertiary);\\n}\\n\\n.dsg-bodyWrap {\\n display: flex;\\n flex-direction: column;\\n}\\n\\n.dsg-card {\\n display: flex;\\n flex-direction: column;\\n gap: 16px;\\n max-height: 360px;\\n margin: 4px 0 4px 4px;\\n padding: 16px 20px;\\n overflow-y: auto;\\n border: 0.5px solid var(--dsw-alias-border-l1);\\n border-radius: 12px;\\n background: var(--dsw-alias-bg-base);\\n}\\n\\n.dsg-list {\\n display: flex;\\n flex-direction: column;\\n gap: 16px;\\n margin: 0;\\n}\\n\\n.dsg-item {\\n display: flex;\\n flex-direction: column;\\n gap: 2px;\\n min-width: 0;\\n}\\n\\n.dsg-question,\\n.dsg-answer,\\n.dsg-verdict {\\n margin: 0;\\n white-space: pre-wrap;\\n overflow-wrap: anywhere;\\n font-size: var(--dsh-content-font-size, 14px);\\n line-height: calc(24px + var(--dsh-content-font-delta, 0px));\\n}\\n\\n.dsg-question {\\n color: var(--dsw-alias-label-tertiary);\\n}\\n\\n.dsg-answer,\\n.dsg-verdict {\\n color: var(--dsw-alias-label-primary);\\n}\\n\\n.dsg-failure {\\n margin: 0;\\n white-space: pre-wrap;\\n overflow-wrap: anywhere;\\n font-size: var(--dsh-content-font-size, 14px);\\n line-height: calc(24px + var(--dsh-content-font-delta, 0px));\\n color: var(--dsw-alias-state-error-primary);\\n}\\n\\n.dsg-answerLine {\\n display: block;\\n}\\n\\n.dsg-skipped {\\n color: var(--dsw-alias-label-tertiary);\\n}\\n\\n.dsg-header {\\n display: block;\\n font-size: var(--dsh-content-font-size-secondary, 13px);\\n color: var(--dsw-alias-label-caption);\\n}\\n\\n.dsg-detail {\\n margin: 2px 0 0;\\n font-size: var(--dsh-content-font-size-secondary, 13px);\\n color: var(--dsw-alias-label-secondary);\\n}\\n\\n.dsg-violations {\\n display: flex;\\n flex-direction: column;\\n gap: 8px;\\n margin: 0;\\n padding-left: 20px;\\n font-size: var(--dsh-content-font-size, 14px);\\n line-height: calc(24px + var(--dsh-content-font-delta, 0px));\\n color: var(--dsw-alias-state-error-primary);\\n}\\n\\n.dsg-raw {\\n max-height: 360px;\\n margin: 4px 0 4px 4px;\\n padding: 12px 16px;\\n overflow: auto;\\n border: 0.5px solid var(--dsw-alias-border-l1);\\n border-radius: 12px;\\n background: var(--dsw-alias-markdown-code-block);\\n font: var(--dsw-font-markdown-code-block-small);\\n white-space: pre-wrap;\\n overflow-wrap: anywhere;\\n color: var(--dsw-alias-label-secondary);\\n}\\n\\n.dsg-inspectButton {\\n display: inline-flex;\\n align-self: flex-start;\\n align-items: center;\\n gap: 4px;\\n margin: 4px 0 2px 4px;\\n padding: 2px 8px;\\n border: 0.5px solid var(--dsw-alias-border-l4);\\n border-radius: 999px;\\n corner-shape: round;\\n background: var(--dsw-alias-bg-base);\\n color: var(--dsw-alias-label-secondary);\\n font-size: 11px;\\n line-height: 16px;\\n opacity: 0;\\n cursor: pointer;\\n transition: opacity 100ms ease;\\n}\\n\\n.dsg-bodyWrap:hover .dsg-inspectButton,\\n.dsg-inspectButton:focus-visible {\\n opacity: 1;\\n}\\n\\n.dsg-inspectButton:hover {\\n background: var(--dsw-alias-interactive-bg-hover-solid);\\n color: var(--dsw-alias-label-primary);\\n}\\n\\n.dsg-visuallyHidden {\\n position: absolute;\\n width: 1px;\\n height: 1px;\\n overflow: hidden;\\n clip: rect(0 0 0 0);\\n white-space: nowrap;\\n}\\n\\n@media (prefers-reduced-motion: reduce) {\\n .dsg-root[data-state='running'] .dsg-row::after {\\n animation: none;\\n }\\n\\n .dsg-inspectButton {\\n transition: none;\\n }\\n}\\n\";", "/** \u672C\u63D2\u4EF6\u5361\u7247\u81EA\u5DF1\u7684\u6587\u6848\u5B57\u5178\u3002\u540D\u5B57\u7A7A\u95F4\u5F52\u672C\u63D2\u4EF6\uFF0C\u590D\u7528\u522B\u4EBA\u7684\u540D\u5B57\u7A7A\u95F4\u4F1A\u5728\u5BF9\u65B9\u6539\u952E\u65F6\u9759\u9ED8\u5931\u6548\u3002 */\n\n/** \u5B57\u5178\u540D\u5B57\u7A7A\u95F4\u3002 */\nexport const NS = 'askGrilling';\n\n/** \u7B80\u4F53\u4E2D\u6587\uFF08\u952E\u96C6\u7684\u4E8B\u5B9E\u6765\u6E90\uFF09\u3002 */\nexport const zh = {\n 'row.title': '\u63D0\u95EE',\n 'row.waiting': '\u7B49\u5F85\u56DE\u7B54',\n 'row.answered': '{answered}/{total} \u5DF2\u56DE\u7B54',\n 'row.skipped': '\u672A\u56DE\u7B54',\n 'row.rejected': '\u5165\u53C2\u88AB\u62D2\u7EDD',\n 'row.cancelled': '\u5DF2\u53D6\u6D88',\n 'row.cancelledDetail': '\u672C\u8F6E\u5DF2\u53D6\u6D88\uFF0C\u672A\u63D0\u4EA4\u56DE\u7B54\u3002',\n 'row.interrupted': '\u5DF2\u4E2D\u65AD',\n 'row.interruptedDetail': '\u672C\u8F6E\u5DF2\u4E2D\u65AD\uFF0C\u672A\u63D0\u4EA4\u56DE\u7B54\u3002',\n 'row.failed': '\u8C03\u7528\u5931\u8D25',\n 'row.violations': '\u6821\u9A8C\u5931\u8D25',\n 'row.inspect': '\u67E5\u770B',\n 'copy': '\u590D\u5236',\n 'copied': '\u5DF2\u590D\u5236',\n 'markdown.footnotes': '\u811A\u6CE8',\n};\n\n/** \u82F1\u6587\uFF0C\u952E\u96C6\u4E0E\u4E2D\u6587\u9010\u6761\u5BF9\u9F50\u3002 */\nexport const en = {\n 'row.title': 'Ask question',\n 'row.waiting': 'waiting for the answer',\n 'row.answered': '{answered}/{total} answered',\n 'row.skipped': 'Not answered',\n 'row.rejected': 'Rejected before asking',\n 'row.cancelled': 'cancelled',\n 'row.cancelledDetail': 'This round was cancelled before answers were submitted.',\n 'row.interrupted': 'interrupted',\n 'row.interruptedDetail': 'This round was interrupted before answers were submitted.',\n 'row.failed': 'Tool call failed',\n 'row.violations': 'Rejected input',\n 'row.inspect': 'Inspect',\n 'copy': 'Copy',\n 'copied': 'Copied',\n 'markdown.footnotes': 'Footnotes',\n};\n"],
5
- "mappings": "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",
3
+ "sources": ["../src/client/index.js", "../src/client/GrillingFormRow.jsx", "../src/recommendation.js", "../src/form-model.js", "../src/client/GrillingFormRow.css", "../src/client/locales.js"],
4
+ "sourcesContent": ["import { GrillingFormRow } from './GrillingFormRow.jsx';\nimport css from './GrillingFormRow.css';\nimport { NS, en, zh } from './locales.js';\n\n/** \u6D4F\u89C8\u5668\u534A\u8FB9\u7684\u5305\u8EAB\u4EFD\uFF0C\u4E5F\u662F bundle \u91CC <style> \u7684\u5F52\u5C5E\u6807\u8BB0\u3002 */\nconst PACKAGE_ID = '@lynn123411/dsh-ask-user-grilling';\nconst CSS_TAG = `${PACKAGE_ID}/GrillingFormRow.css`;\n\n/**\n * \u6D4F\u89C8\u5668\u534A\u8FB9\u8981\u7684 cordis \u670D\u52A1\u3002`locale` \u88C5\u5B57\u5178\uFF0C`slots` \u6CE8\u518C\u884C\uFF1B\n * \u5C11\u58F0\u660E\u4E00\u4E2A\u4E0D\u4F1A\u5F53\u573A\u62A5\u9519\uFF0C\u63D2\u4EF6\u4F1A\u6574\u6761 entry \u53D8 failed\u3001\u4ECE\u9875\u9762\u4E0A\u6D88\u5931\uFF08\u89C1 docs/rules/client-wiring.md\uFF09\u3002\n * \u8F93\u5165\u52A8\u4F5C\u3001\u4F1A\u8BDD\u8BB0\u5F55\u4E0E\u8F93\u5165\u72B6\u6001\u4E0D\u662F\u670D\u52A1\uFF0C\u800C\u662F\u69FD\u4F4D\u7ED9 session \u4F5C\u7528\u57DF\u7EC4\u4EF6\u7684\u6807\u51C6\u5C5E\u6027\uFF0C\u4E0D\u7528\u58F0\u660E\u3002\n */\nexport const inject = ['locale', 'slots'];\n\n/** \u6CE8\u5165\u672C\u63D2\u4EF6\u7684\u6837\u5F0F\u8868\uFF1A\u6309\u6807\u7B7E\u952E\u590D\u7528\u540C\u4E00\u4E2A <style>\uFF0CHMR \u91CD\u8DD1\u65F6\u66FF\u6362\u5185\u5BB9\u800C\u4E0D\u662F\u53E0\u4E00\u4EFD\u3002 */\nfunction injectStyles() {\n if (typeof document === 'undefined') return;\n let tag = document.querySelector('style[data-plugin-css=' + JSON.stringify(CSS_TAG) + ']');\n if (tag === null) {\n tag = document.createElement('style');\n tag.dataset.plugin = PACKAGE_ID;\n tag.dataset.pluginCss = CSS_TAG;\n document.head.appendChild(tag);\n }\n tag.textContent = css;\n}\n\n/**\n * \u6D4F\u89C8\u5668\u534A\u8FB9\uFF1A\u88C5\u672C\u63D2\u4EF6\u7684\u5B57\u5178\uFF0C\u7136\u540E\u6309 wire \u5DE5\u5177\u540D\u6CE8\u518C `grilling_form` \u90A3\u5F20\u8868\u5355\u3002\n * \u6CE8\u518C\u7684\u662F\u81EA\u6709 key\uFF0C\u5B98\u65B9 `ask_user_question` \u90A3\u5F20\u5361\u4E0D\u53D7\u5F71\u54CD\u3002\n * @param {object} ctx - \u5BA2\u6237\u7AEF\u6839\u4E0A\u4E0B\u6587\u3002\n */\nexport function apply(ctx) {\n injectStyles();\n ctx.effect(() => ctx.locale.register(NS, { zh, en }), 'dsh-ask-user-grilling: dictionaries');\n ctx.slots.inject('tool.call.toolview', () => ctx.slots.register({\n name: 'tool.call.toolview',\n key: 'grilling_form',\n locale: NS,\n }, GrillingFormRow));\n}\n", "import { useMemo, useState } from 'react';\nimport {\n Button,\n Checkbox,\n IconDownloadOutlineRegular,\n IconInspectOutlineRegular,\n MarkdownText,\n Tag,\n} from '@deepseek-ai/dsh-client-ui-primitives';\nimport {\n answerLines,\n applyImport,\n applyRecommendedDefaults,\n formStorageKey,\n importBody,\n importStale,\n isReadOnlyAfter,\n readFormModel,\n readStoredForm,\n writeStoredForm,\n} from '../form-model.js';\n\n/**\n * `grilling_form` \u7684 transcript \u884C\uFF1A\u6309 wire \u5DE5\u5177\u540D\u6CE8\u518C\u8FDB `tool.call.toolview`\uFF0C\n * \u4E0E\u5B98\u65B9 `ask_user_question` \u90A3\u5F20\u5361\u5E76\u5217\uFF08\u81EA\u6709 key\uFF0C\u4E0D\u63A5\u7BA1\u5B98\u65B9\u90A3\u5F20\uFF09\u3002\n *\n * \u4E0E\u5B98\u65B9\u5361\u7684\u6839\u672C\u5DEE\u522B\uFF1A\u90A3\u4E00\u5F20\u662F\u300C\u7B49\u7B54\u6848\u7684\u670D\u52A1\u300D\u7684\u56DE\u6267\uFF0C\u8FD9\u4E00\u5F20**\u81EA\u5DF1\u5C31\u662F\u7B54\u9898\u7684\u5730\u65B9**\u3002\n * \u5DE5\u5177\u53D1\u5B8C\u5C31\u7ED3\u675F\u4E86\u672C\u8F6E\uFF0C\u6240\u4EE5\u8868\u5355\u5F97\u5728\u8BB0\u5F55\u91CC\u5C31\u5730\u53EF\u586B\uFF1A\n *\n * - **\u644A\u5F00\u3001\u4E0D\u6298\u53E0\u3001\u6CA1\u6709\u5361\u7247**\uFF1A\u4E0D\u753B\u6298\u53E0\u884C\uFF0C\u4E5F\u4E0D\u5957\u8FB9\u6846/\u80CC\u666F/\u5185\u8FB9\u8DDD/\u5185\u90E8\u6EDA\u52A8\uFF0C\u9898\u76EE\u3001\u9009\u9879\u3001\n * \u6309\u94AE\u8DDF\u4E0A\u4E0B\u6587\u540C\u5904\u4E00\u5C42\uFF0C\u50CF\u6B63\u6587\u81EA\u5DF1\u957F\u51FA\u6765\u7684\u4E00\u6BB5\u3002\u6574\u8F6E\u7684\u8FDB\u5EA6\u7F29\u6210\u9898\u76EE\u533A\u9876\u4E0A\u7684\u4E00\u884C\u5C0F\u7070\u5B57\uFF1B\n * - **\u63A8\u8350\u7684\u9009\u9879\u9ED8\u8BA4\u52FE\u4E0A**\uFF1A\u6A21\u578B\u6807\u4E86\u63A8\u8350\u7684\u9009\u9879\uFF0C\u5728\u7528\u6237\u6CA1\u52A8\u8FC7\u90A3\u9898\u65F6\u5C31\u662F\u9009\u4E2D\u6001\uFF1B\u7528\u6237\u52A8\u8FC7\n * \uFF08\u5305\u62EC\u628A\u9ED8\u8BA4\u9879\u53D6\u6D88\u6389\uFF09\u4E00\u5F8B\u4EE5\u4ED6\u7684\u4E3A\u51C6\uFF1B\n * - \u4F5C\u7B54\u662F\u8FD9\u4E00\u884C\u7684\u672C\u5730\u72B6\u6001\uFF0C\u6309\u4F1A\u8BDD + callId \u5B58\u8FDB localStorage\uFF0C\u5237\u65B0\u540E\u6062\u590D\uFF1B\n * - \u5E95\u90E8\u90A3\u4E2A\u7A84\u6309\u94AE\u628A\u4F5C\u7B54\u5199\u8FDB**\u539F\u751F\u8349\u7A3F**\uFF08\u69FD\u4F4D\u6807\u51C6\u5C5E\u6027 `inputActions.setDraft`\uFF09\uFF0C\n * \u7528\u6237\u6309\u539F\u751F\u53D1\u9001\u952E\u53D1\u51FA\u2014\u2014\u63D2\u4EF6\u4E0D\u78B0\u8F93\u5165\u6846\u3001\u4E5F\u4E0D\u4EE3\u53D1\uFF1B\n * - \u8868\u5355\u91CC\u586B\u4E86\u4E1C\u897F\u800C\u8349\u7A3F\u91CC\u6CA1\u6709\u5BF9\u5E94\u7684\u5BFC\u5165\u5757\u65F6\uFF0C\u884C\u4E0A\u5E38\u9A7B\u4E00\u6761\u63D0\u9192\u3002\u5E73\u53F0\u6CA1\u6709\u53D1\u9001\u524D\u7684\n * \u63D0\u4EA4\u94A9\u5B50\uFF0C\u300C\u53D1\u9001\u524D\u62E6\u622A\u300D\u505A\u4E0D\u5230\uFF0C\u63D0\u9192\u53EA\u80FD\u662F\u63D0\u9192\uFF1B\n * - \u8BB0\u5F55\u91CC\u5728\u8FD9\u6761\u8C03\u7528\u4E4B\u540E\u51FA\u73B0\u4E86\u7528\u6237\u53D1\u8A00\uFF08kind `user`/`steering`\uFF0Cseq \u66F4\u5927\uFF09\uFF0C\u8FD9\u4E00\u8F6E\u5C31\u7B97\n * \u8FC7\u53BB\u4E86\uFF1A\u8868\u5355\u8F6C\u53EA\u8BFB\uFF0C\u5185\u5BB9\u7559\u7740\u7ED9\u4EBA\u770B\uFF0C\u4E0D\u518D\u8FDB\u4E0A\u4E0B\u6587\u3002\n *\n * \u69FD\u4F4D\u7ED9\u7684\u884C\u6570\u636E\uFF1A`{ block, toolName, callId, inspect }` \u4E0E\u53D6\u8BCD\u51FD\u6570 `t`\uFF1B`sessionId`\u3001\n * `inputActions`\u3001`useInput`\u3001`useConversation` \u662F session \u4F5C\u7528\u57DF\u7684\u6807\u51C6\u5C5E\u6027\uFF0C\u968F\u884C\u6570\u636E\u4E00\u8D77\u4EA4\u7ED9\n * \u8FD9\u4E2A\u7EC4\u4EF6\uFF08\u89C1 `@deepseek-ai/dsh-client-ui-conversation` \u7684 slots \u5951\u7EA6\uFF09\u3002\n *\n * @param {object} props - \u69FD\u4F4D\u7ED9\u7684\u884C\u6570\u636E\u4E0E session \u6807\u51C6\u5C5E\u6027\u3002\n * @returns {import('react').JSX.Element} \u8FD9\u4E00\u884C\u7684\u8868\u5355\u3002\n */\nexport function GrillingFormRow({\n block,\n toolName,\n callId,\n sessionId,\n t,\n inspect,\n inputActions,\n useInput,\n useConversation,\n}) {\n const settled = 'kind' in block;\n const argsRaw = (settled ? block.call?.argsRaw : block.argsRaw) ?? '';\n const resultText = settled ? firstText(block.content) : undefined;\n const model = readFormModel({ argsRaw, resultText, failed: settled && block.isError === true });\n const failure = settled && block.isError === true ? failureText(resultText, block.error) : undefined;\n\n const draft = useInput((state) => state.draft);\n const chat = useConversation((snapshot) => snapshot.views.get('chat'));\n const readOnly = isReadOnlyAfter(chat?.legacy?.nodes, callId);\n\n const storageKey = formStorageKey(sessionId);\n /** \u672C\u8F6E\u4F5C\u7B54\u5728\u4F1A\u8BDD storage \u91CC\u7684\u8BFB\u5199\u53E3\uFF1Astorage\u3001\u952E\u3001callId \u4E09\u4E2A\u53C2\u6570\u53EA\u5728\u8FD9\u91CC\u62FC\u4E00\u6B21\u3002 */\n const readForm = () => readStoredForm(storageOf(), storageKey, callId);\n const writeForm = (next) => writeStoredForm(storageOf(), storageKey, callId, next);\n // \u7528\u6237\u81EA\u5DF1\u52A8\u8FC7\u7684\u90A3\u4E00\u4EFD\uFF0C\u8FDE\u4ED6\u300C\u6E05\u7A7A\u8FC7\u8FD9\u9898\u300D\u4E5F\u5728\u91CC\u9762\uFF1B\u4ED6\u6CA1\u52A8\u8FC7\u7684\u9898\u4E0D\u51FA\u73B0\u5728\u8FD9\u91CC\u3002\n const [storedAnswers, setStoredAnswers] = useState(readForm);\n // \u8868\u5355\u4E0A\u663E\u793A\u3001\u5BFC\u5165\u3001\u8BA1\u6570\u7684\u90FD\u662F\u8FD9\u4E00\u4EFD\uFF1A\u7528\u6237\u6CA1\u52A8\u8FC7\u7684\u9898\u4E0A\uFF0C\u63A8\u8350\u9879\u9ED8\u8BA4\u52FE\u7740\u3002\n const answers = applyRecommendedDefaults(model.questions, storedAnswers);\n\n const interactive = model.state === 'form' && !readOnly;\n const body = importBody(model.questions, answers);\n const canImport = interactive && body !== '';\n const stale = canImport && importStale(draft, body);\n const answered = model.questions.filter((question) => answerLines(question, answers[question.id]).length > 0).length;\n\n /** \u8BB0\u4E00\u9053\u9898\u7684\u4F5C\u7B54\uFF1A\u5185\u5B58\u4E0E storage \u4E00\u8D77\u5199\uFF0C\u5237\u65B0\u540E\u624D\u6062\u590D\u5F97\u56DE\u6765\u3002 */\n const setAnswer = (id, next) => {\n const merged = { ...storedAnswers, [id]: next };\n setStoredAnswers(merged);\n writeForm(merged);\n };\n /** \u52FE\u9009 / \u53D6\u6D88\u4E00\u4E2A\u9009\u9879\uFF1A\u754C\u9762\u6309\u5F52\u4E00\u5316\u540E\u7684 label \u8BB0\uFF0C\u540C\u4E00\u4E2A label \u5C31\u662F\u540C\u4E00\u9879\u3002 */\n const toggleOption = (question, label) => {\n const answer = answers[question.id] ?? { selected: [], custom: '' };\n const selected = answer.selected.includes(label)\n ? answer.selected.filter((item) => item !== label)\n : [...answer.selected, label];\n setAnswer(question.id, { ...answer, selected });\n };\n /** \u6BCF\u9898\u7684\u81EA\u586B\u7A7A\uFF1A\u9009\u9879\u8986\u76D6\u4E0D\u5230\u7684\u60C5\u51B5\u5199\u5728\u8FD9\u91CC\u3002 */\n const writeCustom = (question, custom) => {\n const answer = answers[question.id] ?? { selected: [], custom: '' };\n setAnswer(question.id, { ...answer, custom });\n };\n /** \u5BFC\u5165\uFF1A\u628A\u8349\u7A3F\u91CC\u90A3\u6BB5\u5BFC\u5165\u5757\u6362\u6210\u5F53\u524D\u4F5C\u7B54\uFF08\u6CA1\u6709\u5757\u5C31\u653E\u5728\u6700\u524D\u9762\uFF09\uFF0C\u8349\u7A3F\u91CC\u522B\u7684\u5B57\u4E00\u4E2A\u4E0D\u52A8\u3002 */\n const onImport = () => {\n if (!canImport) return;\n inputActions.setDraft(applyImport(draft, body));\n };\n\n return (\n <div className=\"dsg-root\" data-tool={toolName} data-state={model.state} data-readonly={readOnly ? 'true' : undefined}>\n <FormBody\n model={model}\n failure={failure}\n argsRaw={argsRaw}\n resultText={resultText}\n answers={answers}\n answered={answered}\n interactive={interactive}\n readOnly={readOnly}\n stale={stale}\n canImport={canImport}\n t={t}\n onToggleOption={toggleOption}\n onCustom={writeCustom}\n onImport={onImport}\n />\n {inspect === undefined ? null : (\n <button type=\"button\" className=\"dsg-inspectButton\" onClick={inspect}>\n <IconInspectOutlineRegular />\n {t('row.inspect')}\n </button>\n )}\n </div>\n );\n}\n\n/**\n * \u7ED3\u679C\u91CC\u7684\u7B2C\u4E00\u6BB5\u6587\u672C\u3002\u672C\u5DE5\u5177\u53EA\u6E32\u67D3\u4E00\u6BB5\uFF0C\u5F62\u72B6\u4E0D\u5BF9\u5C31\u5F53\u6CA1\u6709\u7ED3\u679C\u3002\n * @param {unknown} content - \u7ED3\u679C content \u6570\u7EC4\u3002\n * @returns {string|undefined} \u6587\u672C\u3002\n */\nfunction firstText(content) {\n if (!Array.isArray(content)) return undefined;\n const text = content.find((block) => block?.type === 'text');\n return typeof text?.text === 'string' ? text.text : undefined;\n}\n\n/**\n * \u5931\u8D25\u884C\u8981\u663E\u793A\u7684\u6587\u5B57\uFF1A\u7ED3\u679C\u91CC\u90A3\u6BB5\u6587\u672C\u4F18\u5148\uFF0C\u6CA1\u6709\u5C31\u7528\u9519\u8BEF\u7684 name/code \u62FC\u4E00\u884C\u3002\n * @param {string|undefined} resultText - \u7ED3\u679C\u6587\u672C\u3002\n * @param {{name?: string, code?: string}|undefined} error - \u7ED3\u679C\u8282\u70B9\u4E0A\u7684\u9519\u8BEF\u3002\n * @returns {string|undefined} \u5931\u8D25\u8BF4\u660E\u3002\n */\nfunction failureText(resultText, error) {\n if (resultText !== undefined && resultText !== '') return resultText;\n const parts = [error?.name, error?.code].filter((part) => typeof part === 'string' && part !== '');\n return parts.length === 0 ? undefined : parts.join(': ');\n}\n\n/** \u6D4F\u89C8\u5668\u91CC\u8BFB localStorage\uFF1B\u9690\u79C1\u6A21\u5F0F\u4E0B\u53D6\u8FD9\u4E2A\u5C5E\u6027\u672C\u8EAB\u5C31\u4F1A\u629B\uFF0C\u53D6\u4E0D\u5230\u5C31\u5F53\u6CA1\u6709\u5B58\u50A8\u3002 */\nfunction storageOf() {\n try {\n return globalThis.localStorage;\n } catch {\n return undefined;\n }\n}\n\n/**\n * \u6B63\u6587\uFF1A\u62D2\u7EDD\u7ED9\u8FDD\u89C4\u6E05\u5355\uFF0C\u5931\u8D25\u7ED9\u5931\u8D25\u8BF4\u660E\uFF0C\u5176\u4F59\u6309\u72B6\u6001\u753B\u8868\u5355\u6216\u53EA\u8BFB\u4F5C\u7B54\u3002\n * @param {object} props - \u8868\u5355\u6A21\u578B\u3001\u4F5C\u7B54\u4E0E\u4EA4\u4E92\u56DE\u8C03\u3002\n * @returns {import('react').ReactNode} \u6B63\u6587\u3002\n */\nfunction FormBody({\n model,\n failure,\n argsRaw,\n resultText,\n answers,\n answered,\n interactive,\n readOnly,\n stale,\n canImport,\n t,\n onToggleOption,\n onCustom,\n onImport,\n}) {\n if (model.state === 'rejected') {\n return (\n <div className=\"dsg-form\" role=\"group\" aria-label={t('row.violations')}>\n <p className=\"dsg-verdict\">{model.error ?? t('row.rejected')}</p>\n <ul className=\"dsg-violations\">\n {model.violations.map((violation, index) => (\n <li key={String(index)}>{violation}</li>\n ))}\n </ul>\n </div>\n );\n }\n if (model.questions.length === 0) {\n // \u8FD0\u884C\u4E2D\u7684\u534A\u622A JSON\u3001\u6216\u4E0D\u662F\u672C\u63D2\u4EF6\u5F62\u72B6\u7684\u7ED3\u679C\uFF1A\u539F\u6837\u7ED9\u51FA\uFF0C\u522B\u88C5\u4F5C\u8BFB\u61C2\u4E86\n const raw = model.state === 'running' ? argsRaw : resultText;\n return raw === undefined || raw === '' ? null : <pre className=\"dsg-raw\">{raw}</pre>;\n }\n if (model.state === 'error') {\n return (\n <div className=\"dsg-form\">\n <p className=\"dsg-failure\">{failure ?? t('row.failed')}</p>\n <QuestionList questions={model.questions} answers={answers} interactive={false} readOnly={false} t={t} onToggleOption={onToggleOption} onCustom={onCustom} />\n </div>\n );\n }\n // \u53EA\u6709\u7ED3\u679C\u843D\u5E93\u3001\u786E\u8BA4\u8FD9\u4E00\u8F6E\u6295\u51FA\u53BB\u4E86\uFF08state 'form'\uFF09\uFF0C\u624D\u6709\u300C\u8FD9\u4E00\u8F6E\u300D\u53EF\u8A00\uFF1A\u53C2\u6570\u8FD8\u5728\u6D41\u5F0F\n // \u4F20\u8F93\uFF08'running'\uFF09\u65F6\u9898\u76EE\u53EF\u80FD\u5DF2\u7ECF\u80FD\u89E3\u6790\uFF0C\u4F46\u8FDB\u5EA6\u3001\u9009\u9879\u4E0E\u5BFC\u5165\u952E\u90FD\u8FD8\u4E0D\u8BE5\u51FA\u73B0\u3002\n const live = model.state === 'form';\n return (\n <div className=\"dsg-form\">\n {live ? <p className=\"dsg-progress\">{t('row.progress', { total: model.questions.length, answered })}</p> : null}\n <QuestionList\n questions={model.questions}\n answers={answers}\n interactive={interactive}\n readOnly={readOnly}\n t={t}\n onToggleOption={onToggleOption}\n onCustom={onCustom}\n />\n {!live ? null : (\n readOnly ? (\n <p className=\"dsg-closed\">{t('row.closed')}</p>\n ) : (\n <div className=\"dsg-footer\">\n {stale ? <p className=\"dsg-reminder\">{t('row.reminder')}</p> : null}\n <Button\n className=\"dsg-import\"\n variant=\"outline\"\n size=\"sm\"\n icon={<IconDownloadOutlineRegular />}\n disabled={!canImport}\n onClick={onImport}\n >\n {t('row.import')}\n </Button>\n </div>\n )\n )}\n </div>\n );\n}\n\n/**\n * \u9898\u76EE\u5217\u8868\uFF1A\u53EF\u4EA4\u4E92\u65F6\u753B\u9009\u9879\u4E0E\u81EA\u586B\uFF1B\u53EA\u8BFB\u65F6\u628A\u5F53\u65F6\u7684\u4F5C\u7B54\u539F\u6837\u5217\u51FA\u6765\uFF1B\u8FD0\u884C\u4E2D\u53EA\u753B\u9898\u5E72\u3002\n * @param {object} props - \u9898\u76EE\u3001\u4F5C\u7B54\u4E0E\u4EA4\u4E92\u56DE\u8C03\u3002\n * @returns {import('react').JSX.Element} \u5217\u8868\u3002\n */\nfunction QuestionList({ questions, answers, interactive, readOnly, t, onToggleOption, onCustom }) {\n const labels = useMemo(() => markdownLabels(t), [t]);\n return (\n <div className=\"dsg-list\">\n {questions.map((question) => (\n <div className=\"dsg-item\" key={question.id}>\n <div className=\"dsg-question\">\n {question.header === '' ? null : <span className=\"dsg-header\">{question.header}</span>}\n {question.question}\n </div>\n {question.detail === undefined ? null : (\n <div className=\"dsg-detail\">\n <MarkdownText text={question.detail} labels={labels} />\n </div>\n )}\n {interactive ? (\n <OptionList question={question} answer={answers[question.id]} t={t} onToggleOption={onToggleOption} onCustom={onCustom} />\n ) : null}\n {readOnly ? <AnswerLine question={question} answer={answers[question.id]} t={t} /> : null}\n </div>\n ))}\n </div>\n );\n}\n\n/**\n * \u4E00\u9053\u9898\u7684\u4F5C\u7B54\u533A\uFF1A\u53EF\u591A\u9009\u7684\u9009\u9879\u884C\u52A0\u4E00\u4E2A\u81EA\u586B\u6846\u3002\n * \u81EA\u586B\u6846\u7528\u539F\u751F textarea\u2014\u2014ui-primitives \u6CA1\u6709\u591A\u884C\u8F93\u5165\u63A7\u4EF6\uFF0C\u5B98\u65B9\u90A3\u5F20\u7B54\u9898\u5361\u4E5F\u662F\u81EA\u5DF1\u753B\u7684\n * textarea\uFF1B\u9664\u5B83\u4E4B\u5916\u7684\u63A7\u4EF6\u90FD\u7528 ui-primitives\u3002\n * @param {object} props - \u9898\u76EE\u3001\u5F53\u524D\u4F5C\u7B54\u4E0E\u56DE\u8C03\u3002\n * @returns {import('react').JSX.Element} \u9009\u9879\u4E0E\u81EA\u586B\u3002\n */\nfunction OptionList({ question, answer, t, onToggleOption, onCustom }) {\n const selected = answer?.selected ?? [];\n return (\n <div className=\"dsg-options\">\n {question.options.map((option) => (\n <div className=\"dsg-optionRow\" key={option.label}>\n <div className=\"dsg-optionMain\">\n <Checkbox\n className=\"dsg-option\"\n checked={selected.includes(option.label)}\n label={option.displayLabel}\n onChange={() => onToggleOption(question, option.label)}\n />\n {option.recommended ? <Tag tone=\"info\" className=\"dsg-badge\">{t('row.recommended')}</Tag> : null}\n </div>\n {option.description === undefined ? null : <p className=\"dsg-optionDescription\">{option.description}</p>}\n </div>\n ))}\n <textarea\n className=\"dsg-custom\"\n rows={2}\n value={answer?.custom ?? ''}\n placeholder={t('row.customPlaceholder')}\n aria-label={`${t('row.customLabel')}\uFF1A${question.question}`}\n onChange={(event) => onCustom(question, event.target.value)}\n />\n </div>\n );\n}\n\n/**\n * \u53EA\u8BFB\u7684\u4E00\u9053\u9898\uFF1A\u628A\u5F53\u65F6\u7684\u52FE\u9009\u4E0E\u81EA\u586B\u5217\u51FA\u6765\uFF0C\u4E00\u6761\u90FD\u6CA1\u6709\u5C31\u662F\u300C\u672A\u4F5C\u7B54\u300D\u3002\n * @param {object} props - \u9898\u76EE\u3001\u4F5C\u7B54\u4E0E\u53D6\u8BCD\u51FD\u6570\u3002\n * @returns {import('react').JSX.Element} \u4F5C\u7B54\u884C\u3002\n */\nfunction AnswerLine({ question, answer, t }) {\n const lines = answerLines(question, answer);\n return (\n <div className=\"dsg-answer\">\n {lines.length === 0 ? (\n <span className=\"dsg-skipped\">{t('row.unanswered')}</span>\n ) : (\n lines.map((line, index) => (\n <span className=\"dsg-answerLine\" key={`${question.id}-${String(index)}`}>{line}</span>\n ))\n )}\n </div>\n );\n}\n\n/**\n * `MarkdownText` \u8981\u7684\u90A3\u4EFD chrome \u6807\u7B7E\uFF0C\u53D6\u81EA\u672C\u63D2\u4EF6\u7684\u5B57\u5178\u3002\n * @param {Function} t - \u672C\u63D2\u4EF6\u7684\u53D6\u8BCD\u51FD\u6570\u3002\n * @returns {{ code: { copyLabel: string, copiedLabel: string }, footnotes: string }} \u6807\u7B7E\u3002\n */\nfunction markdownLabels(t) {\n return {\n code: { copyLabel: t('copy'), copiedLabel: t('copied') },\n footnotes: t('markdown.footnotes'),\n };\n}\n", "// \u63A8\u8350\u6807\u8BB0\u7684\u5F52\u4E00\u5316\u3002\n//\n// \u6A21\u578B\u5199\u63A8\u8350\u7684\u65B9\u5F0F\u4E94\u82B1\u516B\u95E8\uFF1Adescription \u672B\u5C3E\u7684\u300C\uFF1B\u63A8\u8350\u300D\u3001label \u91CC\u4E0D\u5E26\u62EC\u53F7\u7684\u5199\u6CD5\u3001\u65B9\u62EC\u53F7\u3001\n// \u6216\u5E72\u8106\u7ED9\u4E00\u4E2A recommended: true \u5B57\u6BB5\u3002\u754C\u9762\u53EA\u8BA4\u4E00\u79CD\u5F62\u5F0F\u2014\u2014label \u672B\u5C3E\u7684\u300C\uFF08\u63A8\u8350\uFF09\u300D\u540E\u7F00\uFF0C\n// \u4E8E\u662F\u5FBD\u6807\u4E0D\u6E32\u67D3\u3001\u6807\u8BB0\u539F\u6837\u663E\u793A\u6210\u6587\u5B57\u3002\n//\n// \u8FD9\u91CC\u628A\u6807\u8BB0\u7EDF\u4E00\u642C\u5230 label \u672B\u5C3E\u7684\u300C\uFF08\u63A8\u8350\uFF09\u300D\uFF0C\u754C\u9762\u5C31\u80FD\u4E00\u6B21\u8BA4\u51FA\u6765\u3002\u663E\u5F0F\u5B57\u6BB5 recommended\n// \u662F\u53E6\u4E00\u6761\u66F4\u660E\u786E\u7684\u4FE1\u53F7\uFF0C\u4F18\u5148\u4E8E\u6587\u5B57\u3002\n//\n// \u5F52\u4E00\u5316\u540E\u7684 label \u540C\u65F6\u662F**\u9009\u9879\u7684\u8EAB\u4EFD**\uFF1A\u5BBF\u4E3B\u6309\u5B83\u67E5\u540C\u9898\u5185\u91CD\u540D\u3001\u754C\u9762\u6309\u5B83\u5B58\u52FE\u9009\u3002\u6240\u4EE5\u9664\u4E86\n// \u8FD9\u4E2A\u5E26\u540E\u7F00\u7684 label\uFF0C\u8FD8\u7ED9\u51FA cleanLabel\uFF08\u4E0D\u542B\u540E\u7F00\u7684\u5C55\u793A\u6587\u5B57\uFF09\u2014\u2014\u5FBD\u6807\u7531\u754C\u9762\u81EA\u5DF1\u753B\uFF0C\u5BFC\u5165\n// \u5230\u8F93\u5165\u6846\u7684\u4F5C\u7B54\u4E5F\u7528 cleanLabel\uFF0C\u6A21\u578B\u5199\u7684\u63A8\u8350\u6807\u8BB0\u4E0D\u4F1A\u8DDF\u7740\u7528\u6237\u7684\u6D88\u606F\u53D1\u56DE\u53BB\u3002\n\nconst MARKER = 'recommended|\u63A8\u8350';\n// \u534A\u89D2/\u5168\u89D2\u5706\u62EC\u53F7\u4E0E\u65B9\u62EC\u53F7/\u65B9\u5934\u62EC\u53F7\nconst BRACKETED_MARKER = `[\uFF08(\u3010\\\\[]\\\\s*(?:${MARKER})\\\\s*[\uFF09)\u3011\\\\]]`;\n// \u65E0\u62EC\u53F7\u5199\u6CD5\u5FC5\u987B\u7531\u5206\u9694\u7B26\u5F15\u51FA\uFF0C\u5426\u5219\u300C\u67E5\u770B\u63A8\u8350\u300D\u8FD9\u79CD\u6B63\u5E38\u6587\u5B57\u4F1A\u88AB\u8BEF\u5224\nconst SEPARATOR = '\uFF1B;\uFF0C,\u3001:\uFF1A\\\\-\u2014';\nconst TRAILING_PUNCTUATION = '\u3002.\uFF01!\uFF1F?\uFF1B;\uFF0C,\u3001~\uFF5E\\\\s';\n\nconst BRACKETED_SUFFIX = new RegExp(`[\\\\s]*[${SEPARATOR}]?\\\\s*${BRACKETED_MARKER}[${TRAILING_PUNCTUATION}]*$`, 'i');\nconst BARE_SUFFIX = new RegExp(`[\\\\s]*[${SEPARATOR}]\\\\s*(?:${MARKER})[${TRAILING_PUNCTUATION}]*$`, 'i');\n\n/** \u754C\u9762\u4E0A\u63A8\u8350\u9009\u9879\u7684\u6807\u8BB0\u540E\u7F00\uFF1B\u4E5F\u662F\u5F52\u4E00\u5316\u540E label \u7684\u6536\u5C3E\u3002 */\nexport const RECOMMENDED_LABEL_SUFFIX = '\uFF08\u63A8\u8350\uFF09';\n\n/**\n * \u53BB\u6389\u6587\u672C\u672B\u5C3E\u7684\u63A8\u8350\u6807\u8BB0\u3002\n * @param {string} text - label \u6216 description \u539F\u6587\u3002\n * @returns {{ text: string, found: boolean }} \u53BB\u6389\u6807\u8BB0\u540E\u7684\u6587\u672C\uFF0C\u4EE5\u53CA\u662F\u5426\u627E\u5230\u6807\u8BB0\u3002\n */\nexport function stripRecommendationMarker(text) {\n if (typeof text !== 'string') return { text, found: false };\n const match = BRACKETED_SUFFIX.exec(text) ?? BARE_SUFFIX.exec(text);\n if (match === null) return { text, found: false };\n return { text: text.slice(0, match.index).replace(/\\s+$/, ''), found: true };\n}\n\n/**\n * \u628A\u4E00\u4E2A option \u5F52\u4E00\u5316\u6210\u754C\u9762\u80FD\u6E32\u67D3\u7684\u5F62\u5F0F\u3002\n * @param {{ label: string, description?: string, recommended?: boolean }} option - \u6A21\u578B\u7ED9\u7684\u9009\u9879\u3002\n * @returns {{ originalLabel: string, label: string, cleanLabel: string, description?: string, recommended: boolean }}\n * label \u662F\u9009\u9879\u8EAB\u4EFD\uFF08\u5E26\u300C\uFF08\u63A8\u8350\uFF09\u300D\u540E\u7F00\uFF0C\u5BBF\u4E3B\u6309\u5B83\u67E5\u91CD\u3001\u754C\u9762\u6309\u5B83\u5B58\u52FE\u9009\uFF09\uFF1BcleanLabel \u662F\u7ED9\n * \u7528\u6237\u770B\u7684\u6587\u5B57\uFF08recommended \u4E3A true \u65F6\u7531\u754C\u9762\u53E6\u753B\u5FBD\u6807\uFF09\uFF1BoriginalLabel \u662F\u6A21\u578B\u539F\u672C\u5199\u7684 label\u3002\n */\nexport function normalizeOption(option) {\n const label = stripRecommendationMarker(option.label);\n const description = option.description === undefined ? undefined : stripRecommendationMarker(option.description);\n const recommended = option.recommended === true || label.found || (description !== undefined && description.found);\n const cleanLabel = label.text;\n return {\n originalLabel: option.label,\n label: recommended ? `${cleanLabel}${RECOMMENDED_LABEL_SUFFIX}` : cleanLabel,\n cleanLabel,\n description: description === undefined ? undefined : description.text,\n recommended,\n };\n}\n", "// grilling_form \u7684\u5171\u7528\u7EAF\u6A21\u578B\uFF1A\u5BBF\u4E3B\u534A\u8FB9\u7684\u5165\u53C2\u6821\u9A8C\u3001\u754C\u9762\u534A\u8FB9\u7684\u53D6\u6570\u4E0E\u4F5C\u7B54\u5BFC\u5165\uFF0C\u5168\u5728\u8FD9\u4E00\u4E2A\u6A21\u5757\u91CC\u3002\n//\n// \u4E24\u7AEF\u5FC5\u987B\u4E00\u81F4\u7684\u4E24\u4EF6\u4E8B\u90FD\u53EA\u6709\u8FD9\u4E00\u4EFD\u5B9E\u73B0\uFF1A\u9898\u53F7\u5E76\u5165\u6807\u9898\u7684\u62FC\u6CD5\uFF08\u5BBF\u4E3B\u4E0D\u62FC\u3001\u754C\u9762\u62FC\uFF0C\u4F46\u6A21\u578B\u81EA\u5DF1\n// \u5BA3\u5E03\u7684 Q \u7F16\u53F7\u8981\u5BF9\u5F97\u4E0A\uFF09\uFF0C\u4EE5\u53CA\u5F52\u4E00\u5316\u540E\u7684 option label \u8BA4\u9009\u9879\uFF08\u5BBF\u4E3B\u6309\u5B83\u67E5\u91CD\u3001\u754C\u9762\u6309\u5B83\u5B58\u52FE\u9009\u3001\n// \u751F\u6210\u5BFC\u5165\u6587\u672C\u65F6\u518D\u6362\u56DE\u6A21\u578B\u539F\u672C\u5199\u7684 label\uFF09\u3002\u5404\u5199\u4E00\u4EFD\u5FC5\u7136\u6F02\u3002\n//\n// \u7EAF\u51FD\u6570\uFF1A\u4E0D\u78B0 React\u3001\u4E0D\u8BFB\u65F6\u949F\u3001\u4E0D\u8BFB\u4F1A\u8BDD\u3002localStorage \u7531\u8C03\u7528\u65B9\u4F20\u8FDB\u6765\uFF08\u89C1\u6587\u4EF6\u672B\u5C3E\u90A3\u7EC4\u4EE5 storage \u4E3A\u53C2\u6570\u7684\u51FD\u6570\uFF09\u3002\n\nimport { normalizeOption } from './recommendation.js';\n\n/** \u5BFC\u5165\u5757\u7684\u8D77\u6B62\u884C\uFF1A\u91CD\u590D\u5BFC\u5165\u65F6\u9760\u5B83\u8BA4\u51FA\u4E0A\u4E00\u6B21\u5BFC\u5165\u7684\u90A3\u4E00\u6BB5\uFF0C\u539F\u5730\u66FF\u6362\u3002 */\nexport const IMPORT_BLOCK_START = '\u3010\u76D8\u95EE\u4F5C\u7B54\u3011';\nexport const IMPORT_BLOCK_END = '\u3010/\u76D8\u95EE\u4F5C\u7B54\u3011';\n\nfunction isRecord(value) {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction parseJson(text) {\n if (typeof text !== 'string' || text === '') return undefined;\n try {\n return JSON.parse(text);\n } catch {\n return undefined;\n }\n}\n\nfunction stringList(value) {\n return Array.isArray(value) ? value.filter((item) => typeof item === 'string') : [];\n}\n\n/**\n * \u53BB\u6389\u9996\u5C3E\u7A7A\u767D\u540E\u662F\u5426\u4E3A\u7A7A\u3002\u7A7A\u4E32\u4E0E\u7EAF\u7A7A\u767D\u90FD\u4E0D\u503C\u5F97\u9001\u8FDB\u8868\u5355\u3002\n * @param {unknown} value - \u5F85\u5224\u5B9A\u7684\u503C\u3002\n * @returns {boolean} \u975E\u5B57\u7B26\u4E32\u6216\u53BB\u7A7A\u767D\u540E\u4E3A\u7A7A\u65F6\u4E3A true\u3002\n */\nexport function isBlank(value) {\n return typeof value !== 'string' || value.trim() === '';\n}\n\n/**\n * \u628A\u9898\u53F7\u5E76\u8FDB\u6807\u9898\uFF1A\u4E24\u8005\u90FD\u7ED9\u5199\u4F5C `<number> \u00B7 <header>`\uFF0C\u53EA\u7ED9\u4E00\u4E2A\u5C31\u662F\u90A3\u4E00\u4E2A\uFF1B\n * \u6807\u9898\u5DF2\u7ECF\u4EE5\u8BE5\u9898\u53F7\u5F00\u5934\u65F6\u539F\u6837\u7528\u5B83\uFF08\u6A21\u578B\u5E38\u81EA\u5DF1\u5199\u6210 \"Q2 \u00B7 Deadline\"\uFF09\u3002\n * @param {string} [number] - \u9898\u53F7\u3002\n * @param {string} [header] - \u6807\u9898\u3002\n * @returns {string} \u754C\u9762\u6807\u9898\uFF1B\u4E24\u8005\u90FD\u7F3A\u65F6\u4E3A\u7A7A\u4E32\uFF08\u8C03\u7528\u65B9\u636E\u6B64\u9000\u56DE\u9898\u5E72\uFF09\u3002\n */\nexport function mergeNumberIntoHeader(number, header) {\n const num = isBlank(number) ? undefined : number;\n const head = isBlank(header) ? undefined : header;\n if (num === undefined) return head ?? '';\n if (head === undefined) return num;\n return head.startsWith(num) ? head : `${num} \u00B7 ${head}`;\n}\n\n/**\n * \u4E00\u9053\u9898\u5728\u754C\u9762\u4E0E\u5BFC\u5165\u6587\u672C\u91CC\u7684\u6807\u9898\uFF1A\u9898\u53F7\u5E76\u5165\u540E\u7684\u6807\u9898\u4F18\u5148\uFF0C\u6CA1\u6709\u5C31\u7528\u9898\u5E72\uFF0C\u518D\u6CA1\u6709\u5C31\u7528 id\u3002\n * @param {{ id: string, header: string, question: string }} question - \u9898\u76EE\u6A21\u578B\u3002\n * @returns {string} \u6807\u9898\u3002\n */\nexport function titleOf(question) {\n if (!isBlank(question.header)) return question.header;\n if (!isBlank(question.question)) return question.question;\n return question.id;\n}\n\n/** \u5C55\u5F00\u6B63\u6587\u91CC\u4E00\u884C\u7684\u6392\u7248\u7B26\u53F7\uFF1A\u5217\u8868\u7B26\u53F7\uFF08\u542B\u300C1\uFF09\u300D\u8FD9\u79CD\u5168\u89D2\u5199\u6CD5\uFF09\u3001\u5C0F\u6807\u9898\u3001\u5F15\u7528\u5757\uFF0C\u53BB\u6389\u4E4B\u540E\u624D\u770B\u5F97\u51FA\u5B83\u662F\u4E0D\u662F\u4ECE\u9009\u9879\u540D\u5F00\u7684\u5934\u3002 */\nconst LINE_MARKERS = /^\\s*(?:[-*+]\\s+|\\d+[.)\u3001\uFF09]\\s*|#{1,6}\\s+|>\\s*)+/;\n/** \u884C\u5185\u5F3A\u8C03\u7B26\u53F7\uFF1A`**\u7532**`\u3001`*\u7532*` \u4E0E `\u7532` \u662F\u540C\u4E00\u4E2A\u9009\u9879\u540D\u3002 */\nconst INLINE_MARKERS = /[*_`]/g;\n/** \u9009\u9879\u6807\u7B7E\u91CC\u7B2C\u4E00\u4E2A\u5206\u9694\u7B26\u4E4B\u524D\u7684\u90A3\u4E00\u6BB5\uFF0C\u5C31\u662F\u5B83\u5728\u5C55\u5F00\u6B63\u6587\u91CC\u4F1A\u4EE5\u4EC0\u4E48\u6837\u5B50\u5F00\u5934\u3002 */\nconst LABEL_ALIAS_END = /[\uFF1A:\u00B7\u3001\uFF0C,\u3002;\uFF1B!\uFF01?\uFF1F(\uFF08)\uFF09\\-\u2013\u2014\\s]/;\n/** \u522B\u540D\uFF08\u5176\u540E\u53EF\u4EE5\u6709\u4E00\u4E2A\u7A7A\u683C\uFF09\u8DDF\u8FD9\u4E9B\u7B26\u53F7\u624D\u7B97\u540C\u4E00\u4E2A\u6761\u76EE\u3002\u53EA\u8BA4\u300C\u540D\uFF1A\u89E3\u91CA\u300D\u90A3\u4E00\u7C7B\u5206\u9694\u7B26\uFF1A\n * \u987F\u53F7\u4E0E\u9017\u53F7\u4E0D\u7B97\uFF0C\u5426\u5219\u6B63\u6587\u91CC\u300C\u7532\u3001\u4E59\u4E24\u79CD\u505A\u6CD5\u5404\u6709\u4EE3\u4EF7\u300D\u8FD9\u79CD\u6B63\u7ECF\u7684\u5171\u7528\u80CC\u666F\u4F1A\u88AB\u8BEF\u62D2\u3002 */\nconst ALIAS_FOLLOW_CHAR = /[\uFF1A:\u00B7\uFF09)\\-\u2013\u2014>]/;\n\nfunction shorten(text) {\n return text.length <= 60 ? text : `${text.slice(0, 60)}\u2026`;\n}\n\n/**\n * \u4E00\u4E2A\u9009\u9879\u6807\u7B7E\u7684\u522B\u540D\uFF1A\u5230\u7B2C\u4E00\u4E2A\u5206\u9694\u7B26\u4E3A\u6B62\u7684\u90A3\u4E00\u6BB5\u3002\u300C\u7532\uFF1A\u8BE6\u60C5\u9875\u91CC\u7684\u914D\u7F6E\u5165\u53E3\u300D\u2192\u300C\u7532\u300D\uFF0C\n * \u300C\u4FDD\u7559\u73B0\u72B6\u300D\u2192\u300C\u4FDD\u7559\u73B0\u72B6\u300D\u3002\u6A21\u578B\u5728\u5C55\u5F00\u6B63\u6587\u91CC\u91CD\u8FF0\u9009\u9879\u65F6\uFF0C\u5F00\u5934\u5199\u7684\u901A\u5E38\u5C31\u662F\u8FD9\u4E2A\u522B\u540D\u3002\n * @param {string} label - \u5F52\u4E00\u5316\u540E\u7684\u5C55\u793A\u6587\u5B57\uFF08\u4E0D\u542B\u300C\uFF08\u63A8\u8350\uFF09\u300D\u540E\u7F00\uFF09\u3002\n * @returns {string} \u522B\u540D\uFF1B\u7A7A\u6807\u7B7E\u65F6\u4E3A\u7A7A\u4E32\uFF08\u8C03\u7528\u65B9\u8DF3\u8FC7\uFF09\u3002\n */\nfunction labelAlias(label) {\n const cut = label.search(LABEL_ALIAS_END);\n return cut === -1 ? label : label.slice(0, cut);\n}\n\n/**\n * \u5C55\u5F00\u6B63\u6587\u91CC\u6709\u6CA1\u6709\u628A\u67D0\u4E2A\u9009\u9879\u53C8\u5217\u4E86\u4E00\u904D\u3002\u8BA4\u7684\u662F**\u884C\u9996**\uFF1A\u53BB\u6389\u5217\u8868\u7B26\u53F7\u4E0E\u5F3A\u8C03\u7B26\u53F7\u540E\uFF0C\n * \u4E00\u884C\u4EE5\u67D0\u4E2A\u9009\u9879\u7684\u522B\u540D\u5F00\u5934\u3001\u540E\u9762\uFF08\u53EF\u7A7A\u4E00\u683C\uFF09\u7D27\u8DDF\u5206\u9694\u7B26\u6216\u76F4\u63A5\u7ED3\u675F\uFF0C\u5C31\u662F\u540C\u4E00\u4EF6\u4E8B\u5199\u4E86\u7B2C\u4E8C\u904D\u3002\n *\n * \u53EA\u8BA4\u884C\u9996\u3001\u4E14\u8981\u6C42\u522B\u540D\u540E\u9762\u5C31\u662F\u5206\u9694\u7B26\uFF0C\u662F\u4E3A\u4E86\u628A\u8BEF\u62D2\u9762\u538B\u5230\u6700\u5C0F\uFF1A\u6B63\u6587\u91CC\u987A\u53E3\u63D0\u5230\u67D0\u4E2A\u9009\u9879\u540D\n * \uFF08\u300C\u9009\u7532\uFF1A\u2026\u300D\uFF09\u4E0D\u7B97\uFF0C\u53EA\u6709\u300C\u4E00\u6761\u4EE5\u9009\u9879\u540D\u5F00\u5934\u7684\u6761\u76EE\u300D\u624D\u7B97\u3002\u4EE3\u4EF7\u662F\u6F0F\u653E\u4E0E\u8BEF\u62D2\u90FD\u8FD8\u53EF\u80FD\u53D1\u751F\uFF0C\n * \u6240\u4EE5\u6280\u80FD\u6B63\u6587\u90A3\u534A\u8FB9\uFF08\u4E09\u4EFD preset \u91CC grilling \u6280\u80FD\u7684\u5B57\u6BB5\u6620\u5C04\u6BB5\uFF09\u662F\u7B2C\u4E00\u9053\u95F8\u3002\n * @param {string} detail - \u5C55\u5F00\u6B63\u6587\uFF08markdown\uFF09\u3002\n * @param {string[]} aliases - \u8BE5\u9898\u5404\u9009\u9879\u7684\u522B\u540D\u3002\n * @returns {string|undefined} \u547D\u4E2D\u7684\u90A3\u4E00\u884C\uFF1B\u6CA1\u547D\u4E2D\u65F6 undefined\u3002\n */\nfunction detailRestatesOption(detail, aliases) {\n if (isBlank(detail)) return undefined;\n for (const raw of detail.split('\\n')) {\n const line = raw.replace(LINE_MARKERS, '').replace(INLINE_MARKERS, '').trim();\n if (line === '') continue;\n for (const alias of aliases) {\n if (alias === '' || !line.startsWith(alias)) continue;\n const rest = line.slice(alias.length).trimStart();\n if (rest === '' || ALIAS_FOLLOW_CHAR.test(rest[0])) return line;\n }\n }\n return undefined;\n}\n\n/**\n * \u8FD9\u4E00\u8F6E\u7684\u5165\u53C2\u80FD\u4E0D\u80FD\u753B\u6210\u8868\u5355\u3002\u9898\u5E72\u4E0E\u9009\u9879\u7684\u5206\u5DE5\u6574\u4F53\u4E0A\u662F\u5F15\u5BFC\u2014\u2014\u5B50\u4E32\u5339\u914D\u4F1A\u8BEF\u62D2\u5408\u6CD5\u9898\u5E72\uFF0C\n * \u574F\u9898\u5E72\u597D\u8FC7\u5047\u62D2\u7EDD\u2014\u2014\u53EA\u6709\u4E00\u5904\u4F8B\u5916\uFF1A\u5C55\u5F00\u6B63\u6587\u91CC\u628A\u9009\u9879\u53C8\u5217\u4E00\u904D\uFF0C\u90A3\u4F1A\u8BA9\u540C\u4E00\u6279\u9009\u9879\u5728\u9875\u9762\u4E0A\n * \u51FA\u73B0\u4E24\u904D\uFF08\u4E00\u904D\u5E26\u9009\u62E9\u5668\u3001\u4E00\u904D\u4E0D\u5E26\uFF09\uFF0C\u89C1 detailRestatesOption\u3002\n * @param {object[]} questions - \u6A21\u578B\u7ED9\u7684 questions\u3002\n * @returns {string[]} \u9010\u6761\u8FDD\u89C4\u8BF4\u660E\uFF1B\u7A7A\u6570\u7EC4\u8868\u793A\u8FD9\u4E00\u8F6E\u53EF\u4EE5\u6295\u51FA\u53BB\u3002\n */\nexport function validateRound(questions) {\n const violations = [];\n const seenIds = new Set();\n questions.forEach((question, questionIndex) => {\n const where = `Question ${questionIndex + 1} (id ${JSON.stringify(question.id)})`;\n if (typeof question.id === 'string') {\n if (seenIds.has(question.id)) {\n violations.push(`${where} repeats an id already used in this round; the form keys each answer by id, so ids must be unique`);\n }\n seenIds.add(question.id);\n }\n if (isBlank(question.question)) {\n violations.push(`${where} has an empty question text; write the question itself in question and its body in detail`);\n }\n const seenLabels = new Set();\n const aliases = [];\n (question.options ?? []).forEach((option, optionIndex) => {\n const normalized = normalizeOption(option);\n if (isBlank(normalized.label)) {\n violations.push(`${where}, option ${optionIndex + 1} has an empty label; every option needs a label the user can read and pick`);\n return;\n }\n if (seenLabels.has(normalized.label)) {\n violations.push(`${where} has two options that render with the same label ${JSON.stringify(normalized.label)}; the form identifies a choice by its label, so make them differ`);\n return;\n }\n seenLabels.add(normalized.label);\n aliases.push(labelAlias(normalized.cleanLabel));\n });\n const restated = detailRestatesOption(question.detail, aliases);\n if (restated !== undefined) {\n violations.push(`${where} restates its options in detail: the line ${JSON.stringify(shorten(restated))} begins like one of the option labels, so the form would show those options twice; options go only in options, and detail is for what all of them share`);\n }\n });\n return violations;\n}\n\n/**\n * argsRaw \u2192 \u6A21\u578B\u51FA\u7684\u9898\u3002\u89E3\u6790\u5931\u8D25\u3001\u6CA1\u6709 questions\u3001\u6216 id \u91CD\u590D\uFF08\u754C\u9762\u6309 id \u914D\u5BF9\uFF0C\u91CD\u590D\u5373\u65E0\u4ECE\u914D\u5BF9\uFF09\u65F6\u8FD4\u56DE\u7A7A\u6570\u7EC4\u3002\n * @param {unknown} argsRaw - \u8C03\u7528\u7684\u539F\u59CB\u53C2\u6570\u6587\u672C\u3002\n * @returns {object[]} \u9898\u76EE\u6A21\u578B\u3002\n */\nfunction readQuestions(argsRaw) {\n const parsed = parseJson(argsRaw);\n if (!isRecord(parsed) || !Array.isArray(parsed.questions)) return [];\n const questions = [];\n const ids = new Set();\n for (const question of parsed.questions) {\n if (!isRecord(question) || typeof question.id !== 'string' || ids.has(question.id)) return [];\n ids.add(question.id);\n const options = (Array.isArray(question.options) ? question.options : [])\n .filter((option) => isRecord(option))\n .map((option) => {\n const normalized = normalizeOption(option);\n return {\n // \u754C\u9762\u6309 label \u8BA4\u9009\u9879\uFF08\u5BBF\u4E3B\u5DF2\u4FDD\u8BC1\u540C\u9898\u5185\u552F\u4E00\uFF09\uFF0C\u663E\u793A\u7528 cleanLabel\u3001\u5FBD\u6807\u53E6\u753B\uFF0C\n // \u5BFC\u5165\u6587\u672C\u4E5F\u7528 cleanLabel\uFF1A\u6A21\u578B\u5199\u7684\u63A8\u8350\u6807\u8BB0\u4E0D\u4F1A\u8DDF\u7740\u7528\u6237\u7684\u6D88\u606F\u53D1\u56DE\u53BB\u3002\n label: normalized.label,\n displayLabel: normalized.cleanLabel,\n description: normalized.description,\n recommended: normalized.recommended,\n };\n });\n questions.push({\n id: question.id,\n header: mergeNumberIntoHeader(question.number, question.header),\n question: typeof question.question === 'string' ? question.question : '',\n detail: isBlank(question.detail) ? undefined : question.detail,\n options,\n });\n }\n return questions;\n}\n\n/**\n * \u7ED3\u679C\u6587\u672C \u2192 \u8FD9\u4E00\u8F6E\u6295\u51FA\u53BB\u4E4B\u540E\u53D1\u751F\u4E86\u4EC0\u4E48\u3002\n * @param {unknown} resultText - \u5DE5\u5177\u7ED3\u679C\u91CC\u7684\u6587\u672C\u3002\n * @returns {{rejected: boolean, violations: string[], error: string|undefined}|undefined}\n * \u5F62\u72B6\u4E0D\u8BA4\u8BC6\u65F6\u8FD4\u56DE undefined\uFF08\u8C03\u7528\u65B9\u6309\u8C03\u7528\u5931\u8D25\u5904\u7406\uFF09\u3002\n */\nfunction readResult(resultText) {\n const parsed = parseJson(resultText);\n if (!isRecord(parsed)) return undefined;\n if (parsed.rejected === true) {\n return {\n rejected: true,\n violations: stringList(parsed.violations),\n error: typeof parsed.error === 'string' ? parsed.error : undefined,\n };\n }\n if (parsed.pending === true) return { rejected: false, violations: [], error: undefined };\n return undefined;\n}\n\n/**\n * \u8BFB\u51FA\u4E00\u4E2A\u8868\u5355\u884C\u7684\u6A21\u578B\uFF1A\u9898\u9762\u6765\u81EA argsRaw\uFF0C\u72B6\u6001\u6765\u81EA\u7ED3\u679C\u3002\n * @param {{ argsRaw?: unknown, resultText?: unknown, failed?: boolean }} input - argsRaw\uFF1A\u8C03\u7528\u7684\u539F\u59CB\u53C2\u6570\uFF1B\n * resultText\uFF1A\u7ED3\u679C\u6587\u672C\uFF08\u8FD8\u6CA1\u843D\u5E93\u65F6\u4E3A\u7A7A\uFF09\uFF1Bfailed\uFF1A\u8FD9\u6B21\u8C03\u7528\u662F\u5426\u4EE5\u5931\u8D25\u6536\u573A\u3002\n * @returns {{\n * state: 'running'|'form'|'rejected'|'error',\n * questions: object[], violations: string[], error: string|undefined,\n * }} \u8868\u5355\u6A21\u578B\uFF1Bquestions \u4E3A\u7A7A\u8868\u793A\u53C2\u6570\u8FD8\u89E3\u6790\u4E0D\u51FA\u6765\uFF08\u8FD0\u884C\u4E2D\u6216\u4E0D\u662F\u672C\u63D2\u4EF6\u7684\u5F62\u72B6\uFF09\u3002\n */\nexport function readFormModel({ argsRaw, resultText, failed = false }) {\n const questions = readQuestions(argsRaw);\n const result = readResult(resultText);\n let state;\n if (result !== undefined) state = result.rejected ? 'rejected' : 'form';\n else if (failed) state = 'error';\n else if (typeof resultText === 'string' && resultText !== '') state = 'error';\n else state = 'running';\n return {\n state,\n questions,\n violations: result === undefined ? [] : result.violations,\n error: result === undefined ? undefined : result.error,\n };\n}\n\n/**\n * \u5F52\u4E00\u5316 localStorage \u91CC\u8BFB\u56DE\u6765\u7684\u4F5C\u7B54\uFF1A\u53EA\u8BA4 `{[id]: {selected, custom}}` \u8FD9\u4E2A\u5F62\u72B6\u3002\n *\n * **\u5F62\u72B6\u5BF9\u5F97\u4E0A\u7684\u7A7A\u4F5C\u7B54\u4E5F\u7559\u7740**\uFF1A\u4E00\u6761\u300Cselected \u662F\u6570\u7EC4\u3001\u4E14\u4E0E custom \u90FD\u7A7A\u300D\u7684\u8BB0\u5F55\u4E0D\u662F\u810F\u6570\u636E\uFF0C\n * \u800C\u662F\u300C\u7528\u6237\u628A\u8FD9\u9898\u6E05\u7A7A\u4E86\u300D\uFF08\u6700\u5E38\u89C1\u7684\u662F\u53D6\u6D88\u6389\u63A8\u8350\u9879\u7684\u9ED8\u8BA4\u52FE\u9009\uFF09\u3002\u5B83\u4E0E\u300C\u7528\u6237\u6CA1\u52A8\u8FC7\u8FD9\u9898\u300D\u5FC5\u987B\n * \u5206\u5F97\u5F00\uFF0C\u5426\u5219\u9ED8\u8BA4\u52FE\u9009\u4F1A\u5728\u4ED6\u53D6\u6D88\u4E4B\u540E\u53C8\u52FE\u56DE\u6765\u3002\u4E0D\u662F\u8FD9\u4E2A\u5F62\u72B6\u7684\u8BB0\u5F55\uFF08\u6BD4\u5982 selected \u662F\u4E2A\u5B57\u7B26\u4E32\uFF09\n * \u4E00\u5F8B\u4E22\u6389\uFF1A\u90A3\u79CD\u8BB0\u5F55\u4E0D\u662F\u672C\u63D2\u4EF6\u5199\u7684\uFF0C\u8BA4\u6210\u300C\u6E05\u7A7A\u8FC7\u300D\u4F1A\u767D\u767D\u5403\u6389\u9ED8\u8BA4\u52FE\u9009\u3002\n * @param {unknown} value - \u89E3\u6790\u51FA\u6765\u7684\u503C\u3002\n * @returns {object} \u5E72\u51C0\u7684\u4F5C\u7B54\u8868\u3002\n */\nfunction sanitizeAnswers(value) {\n if (!isRecord(value)) return {};\n const answers = {};\n for (const [id, answer] of Object.entries(value)) {\n if (!isRecord(answer)) continue;\n const shaped = Array.isArray(answer.selected) || typeof answer.custom === 'string';\n const selected = stringList(answer.selected);\n const custom = typeof answer.custom === 'string' ? answer.custom : '';\n if (!shaped && selected.length === 0 && custom === '') continue;\n answers[id] = { selected, custom };\n }\n return answers;\n}\n\n/**\n * \u8FD9\u4E00\u8F6E\u91CC\u4E00\u9053\u9898\u7684\u4F5C\u7B54\uFF0C\u9009\u4E2D\u7684\u9879\u6362\u56DE\u7ED9\u4EBA\u770B\u7684\u6587\u5B57\uFF08\u4E0D\u5E26\u300C\uFF08\u63A8\u8350\uFF09\u300D\u540E\u7F00\uFF09\u3002\n * @param {object} question - \u9898\u76EE\u6A21\u578B\u3002\n * @param {{selected?: string[], custom?: string}|undefined} answer - \u8BE5\u9898\u7684\u4F5C\u7B54\u3002\n * @returns {{selected: string[], custom: string}} \u5C55\u793A\u7528\u7684\u4F5C\u7B54\uFF1B\u6CA1\u4F5C\u7B54\u65F6\u4E24\u6837\u90FD\u7A7A\u3002\n */\nexport function answerParts(question, answer) {\n if (!isRecord(answer)) return { selected: [], custom: '' };\n const shown = new Map(question.options.map((option) => [option.label, option.displayLabel]));\n return {\n selected: stringList(answer.selected).map((label) => shown.get(label) ?? label),\n custom: typeof answer.custom === 'string' ? answer.custom.trim() : '',\n };\n}\n\n/**\n * \u4E00\u9053\u9898\u7684\u4F5C\u7B54\u9010\u884C\u5C55\u5F00\uFF08\u9009\u4E2D\u7684\u9879\u5728\u524D\u3001\u81EA\u586B\u7684\u5B57\u5728\u540E\uFF09\u3002\u5BFC\u5165\u6587\u672C\u4E0E\u53EA\u8BFB\u5C55\u793A\u5171\u7528\u8FD9\u4E00\u4EFD\uFF0C\n * \u4E24\u5904\u7684\u884C\u5E8F\u4E0E\u53D6\u820D\u4E0D\u4F1A\u5404\u5199\u4E00\u5957\u3002\n * @param {object} question - \u9898\u76EE\u6A21\u578B\u3002\n * @param {{selected?: string[], custom?: string}|undefined} answer - \u8BE5\u9898\u7684\u4F5C\u7B54\u3002\n * @returns {string[]} \u9010\u884C\u6587\u5B57\uFF1B\u6CA1\u4F5C\u7B54\u65F6\u4E3A\u7A7A\u6570\u7EC4\u3002\n */\nexport function answerLines(question, answer) {\n const { selected, custom } = answerParts(question, answer);\n return [...selected, ...(custom === '' ? [] : [custom])];\n}\n\n/**\n * \u300C\u63A8\u8350\u7684\u9009\u9879\u9ED8\u8BA4\u52FE\u4E0A\u300D\u843D\u5230\u4E00\u4EFD\u4F5C\u7B54\u8868\u4E0A\uFF1A\u7528\u6237\u6CA1\u52A8\u8FC7\u7684\u9898\uFF0C\u6807\u4E86\u63A8\u8350\u7684\u9009\u9879\u5C31\u662F\u9009\u4E2D\u6001\u3002\n * \u7528\u6237\u52A8\u8FC7\uFF08storage \u91CC\u6709\u8FD9\u9898\u7684\u8BB0\u5F55\uFF09\u7684\u9898\u4E00\u5F8B\u4EE5\u4ED6\u7684\u4E3A\u51C6\u2014\u2014\u5305\u62EC\u4ED6\u628A\u9ED8\u8BA4\u9879\u53D6\u6D88\u6389\u3001\u53EA\u7559\u4E00\u4E2A\u7A7A\u8BB0\u5F55\uFF0C\n * \u6240\u4EE5\u8FD9\u91CC\u53EA\u8865**\u7F3A\u5E2D**\u7684\u9898\uFF0C\u7EDD\u4E0D\u8986\u76D6\u5DF2\u6709\u7684\u4F5C\u7B54\u3002\n * @param {object[]} questions - \u9898\u76EE\u6A21\u578B\u3002\n * @param {object} answers - \u7528\u6237\u81EA\u5DF1\u7684\u4F5C\u7B54\u8868\uFF08readStoredForm \u7684\u4EA7\u7269\uFF09\u3002\n * @returns {object} \u8868\u5355\u4E0A\u771F\u6B63\u663E\u793A\u3001\u5BFC\u5165\u3001\u8BA1\u6570\u7684\u4F5C\u7B54\u8868\u3002\n */\nexport function applyRecommendedDefaults(questions, answers) {\n const merged = { ...(isRecord(answers) ? answers : {}) };\n for (const question of questions) {\n if (merged[question.id] !== undefined) continue;\n const selected = question.options.filter((option) => option.recommended).map((option) => option.label);\n if (selected.length > 0) merged[question.id] = { selected, custom: '' };\n }\n return merged;\n}\n\n/**\n * \u751F\u6210\u5BFC\u5165\u6587\u672C\u7684\u6B63\u6587\uFF1A\u9010\u9898\u4E00\u884C\u300C\u6807\u9898\uFF1A\u4F60\u9009\u7684\u9879\uFF0F\u4F60\u81EA\u586B\u7684\u5B57\u300D\uFF0C\u53EA\u542B\u6709\u4F5C\u7B54\u7684\u9898\u3002\n * \u4E00\u884C\u90FD\u6CA1\u6709\u65F6\u8FD4\u56DE\u7A7A\u4E32\uFF08\u8C03\u7528\u65B9\u636E\u6B64\u7981\u7528\u5BFC\u5165\u3001\u4E5F\u4E0D\u663E\u793A\u672A\u5BFC\u5165\u63D0\u9192\uFF09\u3002\n * @param {object[]} questions - \u9898\u76EE\u6A21\u578B\u3002\n * @param {object} answers - \u4F5C\u7B54\u8868\u3002\n * @returns {string} \u6B63\u6587\uFF08\u4E0D\u542B\u5BFC\u5165\u5757\u7684\u8D77\u6B62\u884C\uFF09\u3002\n */\nexport function importBody(questions, answers) {\n const lines = [];\n for (const question of questions) {\n const answer = answerLines(question, answers[question.id]);\n if (answer.length === 0) continue;\n lines.push(`${titleOf(question)}\uFF1A${answer.join('\u3001')}`);\n }\n return lines.join('\\n');\n}\n\n/**\n * \u5BFC\u5165\u5757\u5728\u8349\u7A3F\u91CC\u7684\u4F4D\u7F6E\u3002\u8D77\u6B62\u884C\u4E0D\u914D\u5BF9\u65F6\u6309\u6CA1\u6709\u5757\u5904\u7406\u3002\n * @param {unknown} draft - \u8F93\u5165\u6846\u8349\u7A3F\u3002\n * @returns {{start: number, end: number}|undefined} \u5757\u7684\u7EDD\u5BF9\u533A\u95F4\uFF1Bend \u662F\u6536\u5C3E\u884C\u4E4B\u540E\u7684\u4F4D\u7F6E\u3002\n */\nfunction findImportBlock(draft) {\n if (typeof draft !== 'string') return undefined;\n const start = draft.indexOf(IMPORT_BLOCK_START);\n if (start === -1) return undefined;\n const stop = draft.indexOf(IMPORT_BLOCK_END, start + IMPORT_BLOCK_START.length);\n if (stop === -1) return undefined;\n return { start, end: stop + IMPORT_BLOCK_END.length };\n}\n\nfunction blockTextOf(body) {\n return `${IMPORT_BLOCK_START}\\n${body}\\n${IMPORT_BLOCK_END}`;\n}\n\n/**\n * \u628A\u4F5C\u7B54\u5BFC\u5165\u8349\u7A3F\uFF1A\u8349\u7A3F\u91CC\u5DF2\u6709\u5BFC\u5165\u5757\u5C31\u539F\u5730\u66FF\u6362\u5B83\u672C\u8EAB\uFF08\u5757\u5916\u7684\u5B57\u4E00\u4E2A\u4E0D\u52A8\uFF09\uFF0C\n * \u6CA1\u6709\u5C31\u628A\u5757\u653E\u5728\u6700\u524D\u9762\u3001\u7528\u6237\u539F\u6709\u7684\u6587\u5B57\u6574\u6BB5\u63A5\u5728\u540E\u9762\u3002\n * @param {unknown} draft - \u5F53\u524D\u8349\u7A3F\u3002\n * @param {string} body - importBody \u7684\u4EA7\u7269\uFF1B\u7A7A\u4E32\u65F6\u539F\u6837\u8FD4\u56DE\u8349\u7A3F\u3002\n * @returns {string} \u65B0\u7684\u8349\u7A3F\u6587\u672C\u3002\n */\nexport function applyImport(draft, body) {\n const text = typeof draft === 'string' ? draft : '';\n if (body === '') return text;\n const block = blockTextOf(body);\n const current = findImportBlock(text);\n if (current !== undefined) return text.slice(0, current.start) + block + text.slice(current.end);\n return text === '' ? block : `${block}\\n\\n${text}`;\n}\n\n/**\n * \u8349\u7A3F\u91CC\u90A3\u6BB5\u5BFC\u5165\u5757\u73B0\u5728\u662F\u4E0D\u662F\u5C31\u662F\u8FD9\u4EFD\u4F5C\u7B54\u3002\u8868\u5355\u586B\u4E86\u4E1C\u897F\u5374\u6CA1\u5BFC\u8FDB\u53BB\uFF08\u6216\u5BFC\u5165\u540E\u53C8\u6539\u4E86\uFF09\n * \u5C31\u4E3A true\u2014\u2014\u754C\u9762\u636E\u6B64\u663E\u793A\u672A\u5BFC\u5165\u63D0\u9192\u3002\u5E73\u53F0\u6CA1\u6709\u53D1\u9001\u524D\u7684\u63D0\u4EA4\u94A9\u5B50\uFF0C\u63D0\u9192\u53EA\u80FD\u662F\u63D0\u9192\u3002\n * @param {unknown} draft - \u5F53\u524D\u8349\u7A3F\u3002\n * @param {string} body - importBody \u7684\u4EA7\u7269\uFF1B\u7A7A\u4E32\uFF08\u8868\u5355\u91CC\u4EC0\u4E48\u90FD\u6CA1\u6709\uFF09\u65F6\u6052\u4E3A false\u3002\n * @returns {boolean} \u662F\u5426\u9700\u8981\u63D0\u9192\u3002\n */\nexport function importStale(draft, body) {\n if (body === '') return false;\n const current = findImportBlock(draft);\n if (current === undefined) return true;\n const inner = draft.slice(current.start + IMPORT_BLOCK_START.length, current.end - IMPORT_BLOCK_END.length);\n return inner.replace(/^\\n/, '').replace(/\\n$/, '') !== body;\n}\n\n/**\n * \u8FD9\u4E00\u8F6E\u662F\u5426\u5DF2\u7ECF\u8FC7\u53BB\uFF1A\u8BB0\u5F55\u91CC\u51FA\u73B0\u4E86\u8FD9\u6761\u8C03\u7528\u4E4B\u540E\uFF08seq \u66F4\u5927\uFF09\u7684\u7528\u6237\u53D1\u8A00\u3002\n * \u8BA4\u4E0D\u51FA\u8FD9\u6761\u8C03\u7528\u3001\u6216\u754C\u9762\u8FD8\u62FF\u4E0D\u5230\u8BB0\u5F55\u65F6\u4E00\u5F8B\u8FD4\u56DE false\u2014\u2014\u628A\u8868\u5355\u5F53\u53EA\u8BFB\u5361\u4F4F\u6BD4\u591A\u663E\u793A\u4E00\u6B21\u53EF\u7F16\u8F91\n * \u66F4\u7CDF\uFF08\u7B54\u6848\u6CA1\u5730\u65B9\u5199\uFF09\u3002\n * @param {unknown} nodes - chat \u89C6\u56FE\u7684 legacy.nodes\u3002\n * @param {unknown} callId - \u672C\u6B21\u5DE5\u5177\u8C03\u7528\u7684 callId\u3002\n * @returns {boolean} \u662F\u5426\u8F6C\u53EA\u8BFB\u3002\n */\nexport function isReadOnlyAfter(nodes, callId) {\n if (!Array.isArray(nodes)) return false;\n const mine = nodes.find((node) => isRecord(node) && node.callId === callId);\n if (mine === undefined || typeof mine.seq !== 'number') return false;\n return nodes.some((node) => (\n isRecord(node)\n && (node.kind === 'user' || node.kind === 'steering')\n && typeof node.seq === 'number'\n && node.seq > mine.seq\n ));\n}\n\n// --- \u6301\u4E45\u5316\uFF1Astorage \u7531\u8C03\u7528\u65B9\u4F20\u8FDB\u6765\uFF08\u6D4F\u89C8\u5668\u662F localStorage\uFF0C\u6D4B\u8BD5\u662F\u5047\u4EF6\uFF09\uFF0C\u672C\u6A21\u5757\u4E0D\u81EA\u5DF1\u53D6\u5168\u5C40 ---\n\n/**\n * \u8868\u5355\u72B6\u6001\u5728 storage \u91CC\u7684\u952E\uFF1A\u6309\u4F1A\u8BDD\u5206\u4E00\u4EFD\uFF0C\u4E00\u4EFD\u91CC\u6309 callId \u5B58\u6BCF\u4E00\u8F6E\u7684\u52FE\u9009\u4E0E\u81EA\u586B\u3002\n * @param {unknown} sessionId - \u5F53\u524D\u4F1A\u8BDD id\u3002\n * @returns {string} storage \u952E\u3002\n */\nexport function formStorageKey(sessionId) {\n const scope = typeof sessionId === 'string' && sessionId !== '' ? sessionId : 'unknown-session';\n return `dsh-grilling-form.${scope}`;\n}\n\nfunction readStorage(storage, key) {\n if (storage === undefined || storage === null || typeof storage.getItem !== 'function') return {};\n try {\n const parsed = parseJson(storage.getItem(key));\n return isRecord(parsed) ? parsed : {};\n } catch {\n return {};\n }\n}\n\n/**\n * \u8BFB\u56DE\u672C\u8F6E\u7684\u4F5C\u7B54\u3002storage \u4E0D\u53EF\u7528\u3001\u952E\u4E0D\u5B58\u5728\u3001\u503C\u5F62\u72B6\u4E0D\u5BF9\u90FD\u53EA\u662F\u300C\u4EC0\u4E48\u90FD\u6CA1\u9009\u300D\u3002\n * @param {object|undefined} storage - \u5E26 getItem/setItem \u7684\u5B58\u50A8\uFF08\u53EF\u4E3A undefined\uFF09\u3002\n * @param {string} key - formStorageKey \u7684\u4EA7\u7269\u3002\n * @param {string} callId - \u672C\u6B21\u8C03\u7528\u7684 callId\u3002\n * @returns {object} \u4F5C\u7B54\u8868\u3002\n */\nexport function readStoredForm(storage, key, callId) {\n const all = readStorage(storage, key);\n return sanitizeAnswers(all[callId]);\n}\n\n/**\n * \u5B58\u4E0B\u672C\u8F6E\u7684\u4F5C\u7B54\uFF0C\u4E0D\u52A8\u540C\u4E00\u4E2A\u4F1A\u8BDD\u91CC\u522B\u7684\u8F6E\u6B21\u3002\u5B58\u4E0D\u8FDB\u53BB\uFF08\u914D\u989D\u3001\u9690\u79C1\u6A21\u5F0F\uFF09\u53EA\u8BA9\u8FD9\u4E00\u4EFD\u6D3B\u5728\u5185\u5B58\u91CC\u3002\n * @param {object|undefined} storage - \u5E26 getItem/setItem \u7684\u5B58\u50A8\uFF08\u53EF\u4E3A undefined\uFF09\u3002\n * @param {string} key - formStorageKey \u7684\u4EA7\u7269\u3002\n * @param {string} callId - \u672C\u6B21\u8C03\u7528\u7684 callId\u3002\n * @param {object} answers - \u4F5C\u7B54\u8868\u3002\n * @returns {void}\n */\nexport function writeStoredForm(storage, key, callId, answers) {\n if (storage === undefined || storage === null || typeof storage.setItem !== 'function') return;\n const all = readStorage(storage, key);\n try {\n storage.setItem(key, JSON.stringify({ ...all, [callId]: sanitizeAnswers(answers) }));\n } catch {\n // \u914D\u989D\u6EE1 / \u9690\u79C1\u6A21\u5F0F\uFF1A\u8868\u5355\u7167\u5E38\u5728\u5185\u5B58\u91CC\u80FD\u586B\uFF0C\u53EA\u662F\u5237\u65B0\u540E\u4E0D\u6062\u590D\n }\n}\n", "export default \"/* grilling_form \u7684 transcript \u8868\u5355\u3002\\n *\\n * \u6545\u610F**\u6CA1\u6709\u5361\u7247**\uFF1A\u4E0D\u753B\u6298\u53E0\u884C\uFF0C\u4E5F\u4E0D\u5957\u8FB9\u6846\u3001\u80CC\u666F\u3001\u5185\u8FB9\u8DDD\u4E0E\u5185\u90E8\u6EDA\u52A8\u2014\u2014\u9898\u76EE\u3001\u9009\u9879\u3001\u6309\u94AE\u8DDF\\n * \u4E0A\u4E0B\u6587\u540C\u5904\u4E00\u5C42\uFF0C\u6574\u8F6E\u7684\u8FDB\u5EA6\u7F29\u6210\u9876\u4E0A\u7684\u4E00\u884C\u5C0F\u7070\u5B57\u3002\u6240\u4EE5\u8FD9\u91CC\u9664\u4E86\u90A3\u6761\u5C0F\u7070\u5B57\u4E0E\u81EA\u586B\u6846\u7684\u8F93\u5165\u6846\uFF0C\\n * \u6CA1\u6709\u4EFB\u4F55\u5BB9\u5668\u7EA7\u7684\u7EBF\u6216\u5E95\u8272\uFF1B\u5B57\u53F7\u3001\u884C\u9AD8\u3001\u989C\u8272\u7167\u6284\u5B98\u65B9 @deepseek-ai/dsh-client-ui-tool \u7684\\n * ToolRow / AskQuestionCard\uFF0C\u907F\u514D\u540C\u4E00\u9875\u51FA\u73B0\u4E24\u5957\u89C6\u89C9\u3002 */\\n\\n.dsg-root {\\n display: flex;\\n flex-direction: column;\\n gap: 6px;\\n margin: 4px 0;\\n}\\n\\n.dsg-form {\\n display: flex;\\n flex-direction: column;\\n gap: 16px;\\n}\\n\\n.dsg-progress {\\n margin: 0;\\n font-size: var(--dsh-content-font-size-secondary, 13px);\\n line-height: calc(20px + var(--dsh-content-font-delta, 0px));\\n color: var(--dsw-alias-label-tertiary);\\n}\\n\\n.dsg-list {\\n display: flex;\\n flex-direction: column;\\n gap: 16px;\\n}\\n\\n.dsg-item {\\n display: flex;\\n flex-direction: column;\\n gap: 4px;\\n min-width: 0;\\n}\\n\\n.dsg-question,\\n.dsg-answer,\\n.dsg-verdict {\\n white-space: pre-wrap;\\n overflow-wrap: anywhere;\\n font-size: var(--dsh-content-font-size, 14px);\\n line-height: calc(24px + var(--dsh-content-font-delta, 0px));\\n}\\n\\n.dsg-question {\\n color: var(--dsw-alias-label-primary);\\n}\\n\\n.dsg-answer,\\n.dsg-verdict {\\n color: var(--dsw-alias-label-primary);\\n}\\n\\n.dsg-failure {\\n margin: 0;\\n white-space: pre-wrap;\\n overflow-wrap: anywhere;\\n font-size: var(--dsh-content-font-size, 14px);\\n line-height: calc(24px + var(--dsh-content-font-delta, 0px));\\n color: var(--dsw-alias-state-error-primary);\\n}\\n\\n.dsg-answerLine {\\n display: block;\\n}\\n\\n.dsg-skipped {\\n color: var(--dsw-alias-label-tertiary);\\n}\\n\\n.dsg-header {\\n display: block;\\n font-size: var(--dsh-content-font-size-secondary, 13px);\\n color: var(--dsw-alias-label-caption);\\n}\\n\\n.dsg-detail {\\n margin: 2px 0 0;\\n font-size: var(--dsh-content-font-size-secondary, 13px);\\n color: var(--dsw-alias-label-secondary);\\n}\\n\\n.dsg-options {\\n display: flex;\\n flex-direction: column;\\n gap: 6px;\\n margin-top: 4px;\\n}\\n\\n.dsg-optionRow {\\n display: flex;\\n flex-direction: column;\\n gap: 2px;\\n min-width: 0;\\n}\\n\\n.dsg-optionMain {\\n display: flex;\\n align-items: center;\\n gap: 8px;\\n min-width: 0;\\n}\\n\\n.dsg-option {\\n min-width: 0;\\n font-size: var(--dsh-content-font-size, 14px);\\n line-height: calc(24px + var(--dsh-content-font-delta, 0px));\\n color: var(--dsw-alias-label-primary);\\n}\\n\\n.dsg-badge {\\n flex: none;\\n}\\n\\n.dsg-optionDescription {\\n margin: 0 0 0 24px;\\n font-size: var(--dsh-content-font-size-secondary, 13px);\\n line-height: calc(20px + var(--dsh-content-font-delta, 0px));\\n color: var(--dsw-alias-label-tertiary);\\n}\\n\\n/* \u591A\u884C\u81EA\u586B\uFF1Aui-primitives \u6CA1\u6709\u591A\u884C\u8F93\u5165\u63A7\u4EF6\uFF0C\u5B98\u65B9\u90A3\u5F20\u7B54\u9898\u5361\u4E5F\u662F\u81EA\u5DF1\u753B\u7684 textarea\u3002 */\\n.dsg-custom {\\n box-sizing: border-box;\\n width: 100%;\\n min-height: 56px;\\n margin-top: 2px;\\n padding: 6px 10px;\\n resize: vertical;\\n border: 0.5px solid var(--dsw-alias-border-l4);\\n border-radius: 8px;\\n background: var(--dsw-alias-bg-base);\\n color: var(--dsw-alias-label-primary);\\n font: inherit;\\n font-size: var(--dsh-content-font-size, 14px);\\n line-height: calc(24px + var(--dsh-content-font-delta, 0px));\\n}\\n\\n.dsg-custom::placeholder {\\n color: var(--dsw-alias-label-caption);\\n}\\n\\n.dsg-custom:focus-visible {\\n outline: 2px solid var(--dsw-alias-state-business-primary);\\n outline-offset: 1px;\\n}\\n\\n.dsg-footer {\\n display: flex;\\n flex-direction: column;\\n align-items: flex-end;\\n gap: 6px;\\n}\\n\\n.dsg-import {\\n flex: none;\\n}\\n\\n.dsg-reminder,\\n.dsg-closed {\\n margin: 0;\\n align-self: stretch;\\n font-size: var(--dsh-content-font-size-secondary, 13px);\\n line-height: calc(20px + var(--dsh-content-font-delta, 0px));\\n color: var(--dsw-alias-state-warning-primary, var(--dsw-alias-label-secondary));\\n}\\n\\n.dsg-closed {\\n color: var(--dsw-alias-label-tertiary);\\n}\\n\\n.dsg-violations {\\n display: flex;\\n flex-direction: column;\\n gap: 8px;\\n margin: 0;\\n padding-left: 20px;\\n font-size: var(--dsh-content-font-size, 14px);\\n line-height: calc(24px + var(--dsh-content-font-delta, 0px));\\n color: var(--dsw-alias-state-error-primary);\\n}\\n\\n.dsg-raw {\\n max-height: 360px;\\n margin: 0;\\n padding: 12px 16px;\\n overflow: auto;\\n border: 0.5px solid var(--dsw-alias-border-l1);\\n border-radius: 12px;\\n background: var(--dsw-alias-markdown-code-block);\\n font: var(--dsw-font-markdown-code-block-small);\\n white-space: pre-wrap;\\n overflow-wrap: anywhere;\\n color: var(--dsw-alias-label-secondary);\\n}\\n\\n.dsg-inspectButton {\\n display: inline-flex;\\n align-self: flex-start;\\n align-items: center;\\n gap: 4px;\\n padding: 2px 8px;\\n border: 0.5px solid var(--dsw-alias-border-l4);\\n border-radius: 999px;\\n corner-shape: round;\\n background: var(--dsw-alias-bg-base);\\n color: var(--dsw-alias-label-secondary);\\n font-size: 11px;\\n line-height: 16px;\\n opacity: 0;\\n cursor: pointer;\\n transition: opacity 100ms ease;\\n}\\n\\n.dsg-root:hover .dsg-inspectButton,\\n.dsg-inspectButton:focus-visible {\\n opacity: 1;\\n}\\n\\n.dsg-inspectButton:hover {\\n background: var(--dsw-alias-interactive-bg-hover-solid);\\n color: var(--dsw-alias-label-primary);\\n}\\n\\n@media (prefers-reduced-motion: reduce) {\\n .dsg-inspectButton {\\n transition: none;\\n }\\n}\\n\";", "/** \u672C\u63D2\u4EF6\u8868\u5355\u884C\u81EA\u5DF1\u7684\u6587\u6848\u5B57\u5178\u3002\u540D\u5B57\u7A7A\u95F4\u5F52\u672C\u63D2\u4EF6\uFF0C\u590D\u7528\u522B\u4EBA\u7684\u540D\u5B57\u7A7A\u95F4\u4F1A\u5728\u5BF9\u65B9\u6539\u952E\u65F6\u9759\u9ED8\u5931\u6548\u3002 */\n\n/** \u5B57\u5178\u540D\u5B57\u7A7A\u95F4\u3002 */\nexport const NS = 'grillingForm';\n\n/** \u7B80\u4F53\u4E2D\u6587\uFF08\u952E\u96C6\u7684\u4E8B\u5B9E\u6765\u6E90\uFF09\u3002 */\nexport const zh = {\n 'row.progress': '\u8FD9\u4E00\u8F6E {total} \u9898 \u00B7 \u5DF2\u586B {answered}',\n 'row.closed': '\u672C\u8F6E\u5DF2\u8FC7\uFF0C\u4EC5\u4F9B\u67E5\u770B',\n 'row.rejected': '\u5165\u53C2\u88AB\u62D2\u7EDD',\n 'row.violations': '\u6821\u9A8C\u5931\u8D25',\n 'row.failed': '\u8C03\u7528\u5931\u8D25',\n 'row.unanswered': '\u672A\u4F5C\u7B54',\n 'row.recommended': '\u63A8\u8350',\n 'row.customLabel': '\u81EA\u586B',\n 'row.customPlaceholder': '\u9009\u9879\u4E0D\u591F\u7528\u7684\u8BDD\uFF0C\u5728\u8FD9\u91CC\u81EA\u5DF1\u5199\u4E00\u6BB5',\n 'row.import': '\u5BFC\u5165\u8F93\u5165\u6846',\n 'row.reminder': '\u8868\u5355\u91CC\u586B\u4E86\u4E1C\u897F\uFF0C\u8FD8\u6CA1\u5BFC\u5165\u8F93\u5165\u6846',\n 'row.inspect': '\u67E5\u770B',\n 'copy': '\u590D\u5236',\n 'copied': '\u5DF2\u590D\u5236',\n 'markdown.footnotes': '\u811A\u6CE8',\n};\n\n/** \u82F1\u6587\uFF0C\u952E\u96C6\u4E0E\u4E2D\u6587\u9010\u6761\u5BF9\u9F50\u3002 */\nexport const en = {\n 'row.progress': '{total} question(s) this round \u00B7 {answered} filled',\n 'row.closed': 'This round is over; the form is read-only',\n 'row.rejected': 'Rejected before showing',\n 'row.violations': 'Rejected input',\n 'row.failed': 'Tool call failed',\n 'row.unanswered': 'Not answered',\n 'row.recommended': 'Recommended',\n 'row.customLabel': 'Your own answer',\n 'row.customPlaceholder': 'Write your own answer here if the options do not cover it',\n 'row.import': 'Import to composer',\n 'row.reminder': 'This form has content that is not in the composer yet',\n 'row.inspect': 'Inspect',\n 'copy': 'Copy',\n 'copied': 'Copied',\n 'markdown.footnotes': 'Footnotes',\n};\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
package/lib/index.js CHANGED
@@ -1,25 +1,5 @@
1
1
  // src/index.js
2
2
  import { defineTool } from "@deepseek-ai/dsh-tools";
3
- import "@deepseek-ai/dsh-user-questions";
4
-
5
- // src/contract.js
6
- var RESERVED_ID_PREFIX = "__grill_";
7
- var ROUND_END_QUESTION = Object.freeze({
8
- id: `${RESERVED_ID_PREFIX}round_supplement__`,
9
- question: "\u8FD9\u8F6E\u8FD8\u6709\u4EC0\u4E48\u8981\u8865\u5145\u6216\u8C03\u6574\u7684\u5417\uFF1F",
10
- header: "\u8F6E\u672B\u8865\u5145",
11
- options: Object.freeze([Object.freeze({ label: "\u65E0\u9700\u8865\u5145" })])
12
- });
13
- function isBlank(value) {
14
- return typeof value !== "string" || value.trim() === "";
15
- }
16
- function mergeNumberIntoHeader(number, header) {
17
- const num = isBlank(number) ? void 0 : number;
18
- const head = isBlank(header) ? void 0 : header;
19
- if (num === void 0) return head ?? "";
20
- if (head === void 0) return num;
21
- return head.startsWith(num) ? head : `${num} \xB7 ${head}`;
22
- }
23
3
 
24
4
  // src/recommendation.js
25
5
  var MARKER = "recommended|\u63A8\u8350";
@@ -43,15 +23,82 @@ function normalizeOption(option) {
43
23
  return {
44
24
  originalLabel: option.label,
45
25
  label: recommended ? `${cleanLabel}${RECOMMENDED_LABEL_SUFFIX}` : cleanLabel,
26
+ cleanLabel,
46
27
  description: description === void 0 ? void 0 : description.text,
47
28
  recommended
48
29
  };
49
30
  }
50
31
 
32
+ // src/form-model.js
33
+ function isBlank(value) {
34
+ return typeof value !== "string" || value.trim() === "";
35
+ }
36
+ var LINE_MARKERS = /^\s*(?:[-*+]\s+|\d+[.)、)]\s*|#{1,6}\s+|>\s*)+/;
37
+ var INLINE_MARKERS = /[*_`]/g;
38
+ var LABEL_ALIAS_END = /[::·、,,。;;!!??(())\-–—\s]/;
39
+ var ALIAS_FOLLOW_CHAR = /[::·))\-–—>]/;
40
+ function shorten(text) {
41
+ return text.length <= 60 ? text : `${text.slice(0, 60)}\u2026`;
42
+ }
43
+ function labelAlias(label) {
44
+ const cut = label.search(LABEL_ALIAS_END);
45
+ return cut === -1 ? label : label.slice(0, cut);
46
+ }
47
+ function detailRestatesOption(detail, aliases) {
48
+ if (isBlank(detail)) return void 0;
49
+ for (const raw of detail.split("\n")) {
50
+ const line = raw.replace(LINE_MARKERS, "").replace(INLINE_MARKERS, "").trim();
51
+ if (line === "") continue;
52
+ for (const alias of aliases) {
53
+ if (alias === "" || !line.startsWith(alias)) continue;
54
+ const rest = line.slice(alias.length).trimStart();
55
+ if (rest === "" || ALIAS_FOLLOW_CHAR.test(rest[0])) return line;
56
+ }
57
+ }
58
+ return void 0;
59
+ }
60
+ function validateRound(questions) {
61
+ const violations = [];
62
+ const seenIds = /* @__PURE__ */ new Set();
63
+ questions.forEach((question, questionIndex) => {
64
+ const where = `Question ${questionIndex + 1} (id ${JSON.stringify(question.id)})`;
65
+ if (typeof question.id === "string") {
66
+ if (seenIds.has(question.id)) {
67
+ violations.push(`${where} repeats an id already used in this round; the form keys each answer by id, so ids must be unique`);
68
+ }
69
+ seenIds.add(question.id);
70
+ }
71
+ if (isBlank(question.question)) {
72
+ violations.push(`${where} has an empty question text; write the question itself in question and its body in detail`);
73
+ }
74
+ const seenLabels = /* @__PURE__ */ new Set();
75
+ const aliases = [];
76
+ (question.options ?? []).forEach((option, optionIndex) => {
77
+ const normalized = normalizeOption(option);
78
+ if (isBlank(normalized.label)) {
79
+ violations.push(`${where}, option ${optionIndex + 1} has an empty label; every option needs a label the user can read and pick`);
80
+ return;
81
+ }
82
+ if (seenLabels.has(normalized.label)) {
83
+ violations.push(`${where} has two options that render with the same label ${JSON.stringify(normalized.label)}; the form identifies a choice by its label, so make them differ`);
84
+ return;
85
+ }
86
+ seenLabels.add(normalized.label);
87
+ aliases.push(labelAlias(normalized.cleanLabel));
88
+ });
89
+ const restated = detailRestatesOption(question.detail, aliases);
90
+ if (restated !== void 0) {
91
+ violations.push(`${where} restates its options in detail: the line ${JSON.stringify(shorten(restated))} begins like one of the option labels, so the form would show those options twice; options go only in options, and detail is for what all of them share`);
92
+ }
93
+ });
94
+ return violations;
95
+ }
96
+
51
97
  // src/index.js
52
- var name = "tool-ask-user-grilling";
53
- var inject = ["tools", "userQuestions"];
98
+ var name = "tool-grilling-form";
99
+ var inject = ["tools"];
54
100
  var CONFIG_KEY = "carrier";
101
+ var PENDING_MESSAGE = `The form with this round's questions is now shown in the conversation and this turn has ended. The user has not answered yet: they answer in their own composer, and their answers arrive later as their next user message, one line per answered question ("<title>: <chosen options / typed text>"). Do not wait for it, do not treat this as an answer, and do not write the questions in prose as well.`;
55
102
  function readConfig(config) {
56
103
  if (config === void 0 || config === null) return { carrier: false };
57
104
  if (typeof config !== "object" || Array.isArray(config)) {
@@ -69,13 +116,13 @@ function readConfig(config) {
69
116
  function apply(ctx, config) {
70
117
  if (readConfig(config).carrier) return;
71
118
  ctx.tools.register(defineTool({
72
- name: "ask_user_grilling",
73
- description: "Ask the user a concise question when you need confirmation, a choice, or missing information before proceeding.",
119
+ name: "grilling_form",
120
+ description: "Put this round's grilling questions to the user as a form in the conversation, then end your turn. The user answers it in their own composer and their answers arrive as their next user message. Use it only for a round of per-question grilling, and make it the only and final tool call of the round: it ends the turn, so anything else in the same batch keeps the turn open.",
74
121
  parameters: {
75
122
  questions: {
76
123
  type: "array",
77
124
  required: true,
78
- description: "Questions to ask the user before continuing.",
125
+ description: "The round's questions, in the order the user should read them.",
79
126
  items: {
80
127
  type: "object",
81
128
  additionalProperties: true,
@@ -83,12 +130,12 @@ function apply(ctx, config) {
83
130
  id: {
84
131
  type: "string",
85
132
  required: true,
86
- description: "Stable id for this question; echoed in the answer."
133
+ description: "Stable id for this question; the form keys the user's answer by it."
87
134
  },
88
135
  question: {
89
136
  type: "string",
90
137
  required: true,
91
- description: "The specific question to ask the user."
138
+ description: "The specific question to ask the user. Keep it to the one line that asks; put any longer body in detail."
92
139
  },
93
140
  header: {
94
141
  type: "string",
@@ -96,15 +143,15 @@ function apply(ctx, config) {
96
143
  },
97
144
  number: {
98
145
  type: "string",
99
- description: 'Optional question number, e.g. "Q2". Rendered before the header as "Q2 \xB7 <header>"; use the same Q-number you announced in the message text.'
146
+ description: 'Optional question number, e.g. "Q2". The form renders it before the header as "Q2 \xB7 <header>".'
100
147
  },
101
148
  detail: {
102
149
  type: "string",
103
- description: "Optional supporting body for this question. Rendered as markdown under the question, so put multi-paragraph context here and keep question to the one line that asks."
150
+ description: "Optional supporting body for this question, rendered as markdown under the question. Use it only for what all the options share; a line that begins like one of the option labels is rejected, because the options would then be printed twice (once as prose, once as choices)."
104
151
  },
105
152
  options: {
106
153
  type: "array",
107
- description: 'Optional choices to show the user. If you recommend one, put it first and append "(Recommended)" to that label.',
154
+ description: 'Optional choices the form shows for this question. If you recommend one, put it first and either append "\uFF08\u63A8\u8350\uFF09" to its label or set its recommended flag.',
108
155
  items: {
109
156
  type: "object",
110
157
  additionalProperties: true,
@@ -120,7 +167,7 @@ function apply(ctx, config) {
120
167
  },
121
168
  recommended: {
122
169
  type: "boolean",
123
- description: 'Set true on the option you recommend (alternative to the "(Recommended)" label suffix).'
170
+ description: 'Set true on the option you recommend (alternative to appending "\uFF08\u63A8\u8350\uFF09" to its label). A recommended option is checked by default in the form, so mark only what you would pick yourself.'
124
171
  }
125
172
  }
126
173
  }
@@ -136,7 +183,7 @@ function apply(ctx, config) {
136
183
  properties: {
137
184
  rejected: {
138
185
  type: "boolean",
139
- description: "True when input validation failed and the round was not asked."
186
+ description: "True when input validation failed and no form was shown."
140
187
  },
141
188
  violations: {
142
189
  type: "array",
@@ -147,117 +194,29 @@ function apply(ctx, config) {
147
194
  type: "string",
148
195
  description: "Human-readable error when rejected."
149
196
  },
150
- answers: {
151
- type: "array",
152
- required: true,
153
- description: "One entry per question, in the order asked.",
154
- items: {
155
- type: "object",
156
- additionalProperties: false,
157
- properties: {
158
- id: {
159
- type: "string",
160
- required: true,
161
- description: "The question id you supplied."
162
- },
163
- selected: {
164
- type: "array",
165
- required: true,
166
- items: { type: "string" },
167
- description: "Labels of the options the user picked (may be empty if skipped)."
168
- },
169
- custom: {
170
- type: "string",
171
- description: "User-typed free text for this question, if any."
172
- }
173
- }
174
- }
197
+ pending: {
198
+ type: "boolean",
199
+ description: "True when the form is displayed and this turn has ended; the user has not answered yet."
200
+ },
201
+ message: {
202
+ type: "string",
203
+ description: "What this pending round means for you."
175
204
  }
176
205
  }
177
206
  },
178
207
  render: (_args, value) => [{ type: "text", text: JSON.stringify(value) }]
179
208
  },
180
209
  async execute(args, exec) {
181
- const violations = [];
182
- const seenIds = /* @__PURE__ */ new Set();
183
- args.questions.forEach((question, questionIndex) => {
184
- const where = `Question ${questionIndex + 1} (id ${JSON.stringify(question.id)})`;
185
- if (typeof question.id === "string" && question.id.startsWith(RESERVED_ID_PREFIX)) {
186
- violations.push(`${where} uses the reserved prefix ${RESERVED_ID_PREFIX} (owned by the round-end supplement question)`);
187
- }
188
- if (typeof question.id === "string") {
189
- if (seenIds.has(question.id)) violations.push(`${where} repeats an id already used in this round; ids must be unique because the answer is keyed by id`);
190
- seenIds.add(question.id);
191
- }
192
- if (isBlank(question.question)) {
193
- violations.push(`${where} has an empty question text; write the question itself in question and its body in detail`);
194
- }
195
- const seenLabels = /* @__PURE__ */ new Set();
196
- (question.options ?? []).forEach((option, optionIndex) => {
197
- const label = normalizeOption(option).label;
198
- if (isBlank(label)) {
199
- violations.push(`${where}, option ${optionIndex + 1} has an empty label; every option needs a label the user can read and pick`);
200
- return;
201
- }
202
- if (seenLabels.has(label)) {
203
- violations.push(`${where} has two options that render with the same label ${JSON.stringify(label)}; the form identifies a choice by its label, so make them differ`);
204
- return;
205
- }
206
- seenLabels.add(label);
207
- });
208
- });
210
+ const violations = validateRound(args.questions);
209
211
  if (violations.length > 0) {
210
212
  return {
211
213
  rejected: true,
212
214
  violations,
213
- error: `${violations.length} violation(s) in this round: ids must be unique and must not use the reserved ${RESERVED_ID_PREFIX} prefix, question text must not be blank, and the options of one question must not share a label. Fix them and call this tool again.`,
214
- answers: []
215
+ error: `${violations.length} violation(s) in this round: ids must be unique, question text must not be blank, and the options of one question must not share a label. Fix them and call this tool again.`
215
216
  };
216
217
  }
217
- const labelRestore = /* @__PURE__ */ new Map();
218
- const questions = args.questions.map((question) => {
219
- const header = mergeNumberIntoHeader(question.number, question.header);
220
- const restore = /* @__PURE__ */ new Map();
221
- const options = (question.options ?? []).map((option) => {
222
- const normalized = normalizeOption(option);
223
- if (normalized.label !== normalized.originalLabel) {
224
- restore.set(normalized.label, normalized.originalLabel);
225
- }
226
- return {
227
- label: normalized.label,
228
- ...normalized.description !== void 0 ? { description: normalized.description } : {}
229
- };
230
- });
231
- labelRestore.set(question.id, restore);
232
- return {
233
- id: question.id,
234
- question: question.question,
235
- ...header !== "" ? { header } : {},
236
- ...isBlank(question.detail) ? {} : { detail: question.detail },
237
- options,
238
- multiSelect: true
239
- };
240
- });
241
- questions.push({
242
- ...ROUND_END_QUESTION,
243
- options: ROUND_END_QUESTION.options.map((option) => ({ ...option })),
244
- multiSelect: true
245
- });
246
- const answer = await ctx.userQuestions.ask({
247
- questions,
248
- ...exec.agent !== void 0 ? { agent: exec.agent } : {},
249
- signal: exec.signal
250
- });
251
- return {
252
- answers: answer.answers.map((entry) => {
253
- const restore = labelRestore.get(entry.id);
254
- return {
255
- id: entry.id,
256
- selected: entry.selected.map((label) => restore?.get(label) ?? label),
257
- ...entry.custom !== void 0 ? { custom: entry.custom } : {}
258
- };
259
- })
260
- };
218
+ exec.concludeTurn();
219
+ return { pending: true, message: PENDING_MESSAGE };
261
220
  }
262
221
  }));
263
222
  }