@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/README.md +28 -24
- package/assets/icon.svg +5 -0
- package/cordis.patch.yml +1 -1
- package/lib/client.js +352 -196
- package/lib/client.js.map +3 -3
- package/lib/index.js +90 -131
- package/lib/index.js.map +3 -3
- package/locale/en.json +6 -0
- package/locale/zh.json +6 -0
- package/package.json +11 -10
package/README.md
CHANGED
|
@@ -1,35 +1,39 @@
|
|
|
1
1
|
# @lynn123411/dsh-ask-user-grilling
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
grilling 轮的逐题盘问工具:把整轮问题作为一张**摊开在对话记录里的可交互表单**(没有折叠行、没有卡片壳,题目跟上下文同处一层)投出即结束本轮;用户在表单里勾选、自填,点表单底部的「导入输入框」写进**原生输入框草稿**,再按原生发送键发出,作答随后作为**下一条用户消息**到达。它不出文本、不阻塞、不接管输入框,与原生 `ask_user_question`([`@deepseek-ai/dsh-tool-ask-user`](https://www.npmjs.com/package/@deepseek-ai/dsh-tool-ask-user))是两件事:原生那条在输入框上方接管作答面板、阻塞等答案,留给一次简单确认;本插件只负责 grilling 轮的逐题盘问。三个 `matt-*` preset 两行并存:原生 `- id: tool-ask-user` 行原样保留,旁边新增 `- id: tool-grilling-form` 行。
|
|
4
4
|
|
|
5
|
-
> ⚠️ **装配是一个包两条行**:preset 工具行注册工具;profile `package.json` 的 `dsh.profile.bundles` 里还要有本包(包内 `cordis.patch.yml` 插入一条 `config.carrier: true`
|
|
5
|
+
> ⚠️ **装配是一个包两条行**:preset 工具行注册工具;profile `package.json` 的 `dsh.profile.bundles` 里还要有本包(包内 `cordis.patch.yml` 插入一条 `id: lynn-grilling-form-carrier`、`config.carrier: true` 的载体行,宿主半边在那条行下什么都不注册),Web shell 才服务本包的浏览器半边。少了后一条,工具照常可用、transcript 那一行退回原始 JSON。
|
|
6
6
|
|
|
7
7
|
## 特性
|
|
8
8
|
|
|
9
|
-
- **
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
9
|
+
- **grilling_form**:一次调用只做三件事——校验入参;通过校验就 `exec.concludeTurn()` 标记本轮结束;返回 `{ pending: true, message }`,让模型知道表单已展示、答案会晚到、别把这次调用当成已回答、也别再把题目写一遍散文。入参不合格就返回 `rejected` + `violations` 且**不标记结束**,模型可在同一轮改完重调。
|
|
10
|
+
- **投出即结束本轮**:工具不等待答案、不接管输入框;描述与技能正文都要求它是本轮的**唯一且最后**一次调用(同一批里别的调用会把本轮撑开)。
|
|
11
|
+
- **入参校验只拦表单表示不了的东西**:同一轮里重复的 id、空白题干、空白选项 label、同一题里归一化后同名的两个选项,以及**展开正文里把选项又列了一遍**(那会让页面上出现两遍选项:一遍带选择器、一遍不带)——命中即就地拒绝;除此之外题干与选项的分工是引导,不做子串校验;id 是普通字符串,没有保留前缀。
|
|
12
|
+
- **强制多选**:所有问题一律多选,schema 里没有 `multi_select` 开关;这一行为刻意不写进工具描述。
|
|
13
|
+
- **题号并入标题**:可选参数 `number`(如 `"Q2"`)由界面并进 `header`,格式 `<number> · <header>`,只给一个时就是那一个;`header` 已经以该题号开头就原样送出,不出现「`Q2 · Q2 · …`」这种重复前缀。拼法只有 `src/form-model.js` 一份,宿主与界面不会各拼一套。
|
|
14
|
+
- **正文只写共用的背景**:可选参数 `detail` 由表单按 markdown 渲染在题干下方,**只写各选项共用的背景**;某个选项自己的取舍写进它自己的 `description`。模型把选项连同代价在 `detail` 里再讲一遍时,行首会与某个选项标签同形,宿主当场拒收并让它改——这是「页面上只留带选择器的那一遍」的硬保证。认法:一行去掉列表符号与强调符号后,以选项标签的第一个分隔符之前那一段开头,紧随(可空一格)`:` `:` `·` `—` 这类「名:解释」分隔符或直接结束。顿号与逗号不算(正文里「甲、乙两种做法各有代价」这种共用背景不该被误拒),所以「甲,详情…」这种写法会漏放;误拒的代价是模型改一轮重投,技能正文那半边才是第一道闸。
|
|
15
|
+
- **推荐标记归一化**:模型把推荐写成 `description` 末尾的「;推荐」、`label` 里的无括号写法,或直接给 `recommended: true`,都由 `src/recommendation.js` 统一搬到 label 末尾的「(推荐)」,界面按它画「推荐」徽标、不把标记原样显示成文字;导入文本与只读展示用不带后缀的干净文字,用户的消息不会把「(推荐)」发回去。
|
|
16
|
+
- **推荐项默认勾上**:标了推荐的选项在用户没动过那题时就是选中态,因此它一进页面就算作答——计数、导入按键、「还没导入」提醒都照它走。用户动过那题(包括把默认项取消掉)一律以他的为准;「取消过」与「没作答」分得开,靠的是持久化里那条空记录不会被清掉。
|
|
17
|
+
- **表单摊开、不折叠(本插件的浏览器半边)**:不画折叠行,也不套卡片边框、背景、内边距与内部滚动——题目、选项、按钮跟上下文同处一层,像正文自己长出来的一段;整轮的进度缩成题目区顶上的一行小灰字「这一轮 N 题 · 已填 M」,转只读后是「本轮已过,仅供查看」。按 wire 工具名注册自有 key `grilling_form`,官方 `ask_user_question` 那张卡不受影响。展开的正文是整轮题目——题号标题、题干、`detail` markdown、可多选的勾选项(推荐项带徽标)、每题一个自填 textarea。
|
|
18
|
+
- **没有提交键**:表单底部只有一个窄的「导入输入框」按键,把作答写进原生草稿(槽位标准属性 `inputActions.setDraft`);发送权只在原生输入框的发送键上,插件不代发。导入文本是逐题一行「标题:选的项/自填的字」,整段包在 `【盘问作答】` / `【/盘问作答】` 之间;草稿里已有导入块就原地替换它,块外用户手打的字一个不动,没有块就放在最前面。
|
|
19
|
+
- **未导入提醒**:表单里填了东西而草稿里没有对应的导入块时,行上常驻一条提醒。平台没有发送前的提交钩子,「发送前拦截」做不到,提醒只能是提醒。
|
|
20
|
+
- **只读**:这条调用之后记录里出现过用户发言(`kind` 为 `user` / `steering`、`seq` 更大)就算本轮过去,表单转只读——内容留着给人看,不再可填、也不再进上下文。
|
|
21
|
+
- **按会话持久化**:勾选与自填按会话 + `callId` 存进 `localStorage`(键 `dsh-grilling-form.<sessionId>`),刷新后恢复;一条「什么都没选」的记录也是记录(用户把这题清空了),不会被当成脏数据丢掉;storage 不可用或写不进去只让这一份活在内存里。
|
|
22
|
+
- **流式与失败不假装读懂**:参数还在流式传输(`argsRaw` 解析不出来)时退回原始文本;结果形状不认识画调用失败;被拒画违规清单。
|
|
23
|
+
- **预设里的分工(`matt-standard` / `matt-ptc` / `matt-cordis`)**:原生 `ask_user_question` 管一次简单确认,本插件管 grilling 轮的逐题盘问;三份 preset 的 `description` 与 `skills/grilling/SKILL.md` 要求一轮只投一次表单、不写散文预告,`grilling_form` 就是该轮唯一且最后的调用(ptc 形态是 `run_code` 程序内的 `return await tools.grilling_form({...})`)。技能正文里那段字段映射写死了「选项只写进 `options`」:`detail` 只承载各选项共用的背景,每个选项自己的取舍写进它自己的 `description`。
|
|
21
24
|
|
|
22
25
|
## 自检
|
|
23
26
|
|
|
24
27
|
`pnpm check`(= `pnpm build && pnpm test && pnpm assert:lib-untracked`):
|
|
25
28
|
|
|
26
|
-
- `scripts/test-recommendation.mjs
|
|
27
|
-
- `scripts/test-
|
|
28
|
-
- `scripts/smoke-host.mjs`:真 cordis `Context`、真 `ctx.tools
|
|
29
|
-
- `scripts/test-render.mjs`:用真 react
|
|
30
|
-
- `scripts/smoke-client.mjs`:按 Web shell 的方式加载构建出来的 `lib/client.js`(`window.__ModuleLoader__.load` 桩件),用带内核 inject 守卫的假上下文断言 `tool.call.toolview` 的 keyed
|
|
29
|
+
- `scripts/test-recommendation.mjs`:推荐标记的归一化——各种写法统一成 label 末尾的「(推荐)」、干净 label 与原始 label 各自保留,正常以「推荐」收尾的文字不误判;
|
|
30
|
+
- `scripts/test-form-model.mjs`:共用纯模型(题号并入标题、入参校验含「展开正文重述选项」的认法与不误伤、推荐项默认勾选与「取消过 ≠ 没作答」、导入文本与导入块的新增与原地替换、未导入提醒、只读判定、按会话持久化与脏数据清理);
|
|
31
|
+
- `scripts/smoke-host.mjs`:真 cordis `Context`、真 `ctx.tools`(`ToolRuntime`:工具经它注册、再经它取回),执行上下文是假件、只记录 `concludeTurn` 有没有被喊——工具不再等答案,所以这条链上没有假应答者;断言导出身份 `tool-grilling-form` / `inject: ['tools']`、工具名 `grilling_form`、立即返回 `pending`(不带 `callId` / `answers`)、每一种入参拒绝且**不标记结束**、`__grill_` 前缀不再被拒、字段说明把两条新规则讲给模型、缺字段由 `defineTool` 拦住,以及载体行什么都不注册与 config 校验(未知键、错类型当场抛);
|
|
32
|
+
- `scripts/test-render.mjs`:用真 react 静态渲染构建出来的行组件(`ui-primitives` 是替身,真组件外观在浏览器里看),断言摊开不折叠(没有折叠控件、没有卡片壳)、推荐项默认勾上、取消后不勾回来、可交互与只读两态、导入按键与禁用态、未导入提醒、推荐徽标、被拒与失败、流式原始文本,取词走真字典、缺键或缺参数即失败;
|
|
33
|
+
- `scripts/smoke-client.mjs`:按 Web shell 的方式加载构建出来的 `lib/client.js`(`window.__ModuleLoader__.load` 桩件),用带内核 inject 守卫的假上下文断言 `tool.call.toolview` 的 keyed 注册与字典(守卫自带反例断言),守住 `dsg-*` class 名在 JSX 与 CSS 之间不单边漂移,并核对引到的 ui-primitives 名字都在安装副本的导出表里;
|
|
34
|
+
- `scripts/assert-lib-untracked.mjs`:`lib/` 是构建镜像,`git ls-files lib` 必须为空。
|
|
31
35
|
|
|
32
|
-
|
|
36
|
+
自检要的内核包(`cordis` / `dsh-tools` / `dsh-system-prompt`)与 `ui-primitives` 安装副本是本插件的 devDependencies(与 `engines.dsh` 同版本),`pnpm install` 后离线可跑、不指向任何 DSH 安装副本。`src/` 是源码,`lib/` 是构建镜像、不入库(`pnpm build` 现打,`prepack` 在发布前打一次)。
|
|
33
37
|
|
|
34
38
|
## 安装
|
|
35
39
|
|
|
@@ -37,13 +41,13 @@ DSH 原生 `ask_user_question`([`@deepseek-ai/dsh-tool-ask-user`](https://www.
|
|
|
37
41
|
dsh plugin --profile web add @lynn123411/dsh-ask-user-grilling
|
|
38
42
|
```
|
|
39
43
|
|
|
40
|
-
已发布至 npm,供三个 matt 预设(`matt-standard` / `matt-ptc` / `matt-cordis
|
|
44
|
+
已发布至 npm,供三个 matt 预设(`matt-standard` / `matt-ptc` / `matt-cordis`)使用:三份 preset 的 `config.plugins` 里,上游 `- id: tool-ask-user` 行原样保留,旁边新增 `- id: tool-grilling-form` 行消费本包。三份 preset 把本包声明为 `dependencies`(preset 行因此总可解析);但 `dsh.profile.bundles` 只按 profile 的**直接依赖**登记,安装命令仍要点名本包:`dsh plugin --profile web add @lynn123411/dsh-preset-matt-<id> @lynn123411/dsh-ask-user-grilling`(开发副本同理带上 `./plugins/dsh-ask-user-grilling`;`link:` 包的 `dependencies` 不会被物化)。
|
|
41
45
|
|
|
42
|
-
**本地开发**:用 `link:`,不要拷贝——运行副本软链接到本仓库目录后,仓库里的文件就是“已安装内容”:`cd ~/.dsh/profiles/web && pnpm add "link:<my-dsh 绝对路径>/plugins/dsh-ask-user-grilling" --offline`,再在插件目录 `pnpm install && pnpm build`。本插件宿主半边 `import` 了 `@deepseek-ai/dsh-tools`
|
|
46
|
+
**本地开发**:用 `link:`,不要拷贝——运行副本软链接到本仓库目录后,仓库里的文件就是“已安装内容”:`cd ~/.dsh/profiles/web && pnpm add "link:<my-dsh 绝对路径>/plugins/dsh-ask-user-grilling" --offline`,再在插件目录 `pnpm install && pnpm build`。本插件宿主半边 `import` 了 `@deepseek-ai/dsh-tools`(不再依赖任何提问服务),浏览器半边 `import` 了 `@deepseek-ai/dsh-client-ui-primitives`;`link:` 插件的这些内核包由 DSH 自身路由供给(前提是 DSH 带 `--preserve-symlinks` 启动,启动器已默认带上——见仓库 `docs/rules/dev-copy.md`「内核包路由与 `--preserve-symlinks`」)。**不要**手工拷贝进 `node_modules/`:裸拷贝是未注册状态,`pnpm install` / `dsh plugin add|remove` 等任何 profile 同步都会把它当 extraneous 清掉,而 roster 对每份 preset 做行可解析性健康检查——插件一旦被剪,引用它的三份 matt preset 会整体从模式选择里消失。
|
|
43
47
|
|
|
44
48
|
**profile 侧两处登记,缺一不可**:
|
|
45
49
|
|
|
46
|
-
1. `dsh.profile.bundles` 里有 `@lynn123411/dsh-ask-user-grilling`。包内 `cordis.patch.yml` 会在 bundle 层插入一条 `id: lynn-
|
|
47
|
-
2. preset 的组合里有工具行(`- id: tool-
|
|
50
|
+
1. `dsh.profile.bundles` 里有 `@lynn123411/dsh-ask-user-grilling`。包内 `cordis.patch.yml` 会在 bundle 层插入一条 `id: lynn-grilling-form-carrier`、`config: { carrier: true }` 的行;宿主半边在那条行下什么都不注册。这一条只为让 Web shell 服务本包的浏览器半边:客户端 bundle 只对 root loader 的 **enabled entry** 服务,而 preset 工具行是 `ctx.plugin` 直接挂的子树、不是 Loader entry。位置决定功能——少了它工具照常可用,只是 transcript 那一行退回原始 JSON;`carrier` 之外的 config 键或错类型当场抛,免得拼错的载体行静默把工具注册成 root layer 的全局工具。
|
|
51
|
+
2. preset 的组合里有工具行(`- id: tool-grilling-form` / `name: '@lynn123411/dsh-ask-user-grilling'`)。preset 由组合包的 patch 声明,这一行放在 `@deepseek-ai/dsh-agent-preset` 的 `config.plugins` 里;工具与它的作用域由这一行注册。
|
|
48
52
|
|
|
49
53
|
改完 `src/` 必须重新 `pnpm build`:宿主半边与**新加入 profile bundle 层的这一行**要重启 DSH 实例(bundle 层在启动时组装);已经在图里的客户端 bundle 由 `dsh-client-hmr` 轮询到改动后热换,页面自行重载,必要时硬刷新一次。
|
package/assets/icon.svg
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
|
|
2
|
+
<path d="M3.5 6.5A2.5 2.5 0 0 1 6 4h9a2.5 2.5 0 0 1 2.5 2.5v5A2.5 2.5 0 0 1 15 14H9l-3.5 3v-3H6a2.5 2.5 0 0 1-2.5-2.5z"/>
|
|
3
|
+
<path d="M9.5 7.9a1.95 1.95 0 0 1 3.9.55c0 1.25-1.95 1.45-1.95 2.65"/>
|
|
4
|
+
<path d="M11.45 13.05v.05"/>
|
|
5
|
+
</svg>
|
package/cordis.patch.yml
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
# mount 说明与 client-modules 的 processOne)。所以它必须什么都不注册——工具仍由 preset 行
|
|
6
6
|
# 注册,否则工具会以 root layer 的身份对每一个 preset 的 agent 可见。
|
|
7
7
|
- insert:
|
|
8
|
-
- id: lynn-
|
|
8
|
+
- id: lynn-grilling-form-carrier
|
|
9
9
|
name: '@lynn123411/dsh-ask-user-grilling'
|
|
10
10
|
config:
|
|
11
11
|
carrier: true
|