@deepseek-ai/dsh-client-ui-user-questions 0.1.5-rc.2 → 0.1.6-alpha.2

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.i18n.yaml CHANGED
@@ -2,5 +2,5 @@
2
2
  # side as of the last confirmed-consistent state. Both languages carry equal authority;
3
3
  # after editing either side, bring the other along and re-record with:
4
4
  # pnpm run verify-translation-pairing --write packages/client/ui-user-questions/README.md
5
- README.md: 9e5997eb2ebe69b4b2d49b57a61b0bd36ec41053
6
- README.zh.md: a41ace332376ca7ed5e5d52c9e54c01244950383
5
+ README.md: abbf855706b478857540b12712c5999082b0ab22
6
+ README.zh.md: 995035cbb08ff6c64e2b91b61ab49b2bd473b9b7
package/README.md CHANGED
@@ -9,7 +9,7 @@ English | [中文](README.zh.md)
9
9
 
10
10
  ## Summary
11
11
 
12
- When an agent asks a question in the Web client, this package replaces the chat composer with an interactive question surface. Users can move through questions, choose one or multiple options, enter custom answers, skip items, and submit one structured answer batch. Single-choice selections advance immediately, while drafts survive Session navigation for the lifetime of the page. A single question with a supported presentation intent can use a dedicated surface, including the plan-review card with `Chat about it`, `Refuse`, and `Approve` actions.
12
+ When an agent asks a question in the Web client, this package replaces the chat composer with an interactive question surface. Users can move through questions, choose one or multiple options, enter custom answers, skip items, and submit one structured answer batch. Single-choice selections advance immediately, while drafts survive Session navigation for the lifetime of the page. A single question with a supported presentation intent can use a dedicated surface, including the plan-review card with `Request changes` and `Approve` actions.
13
13
 
14
14
  ## Table of Contents
15
15
 
@@ -29,15 +29,15 @@ When the agent asks a question, the composer becomes the question surface: answe
29
29
 
30
30
  ### Answering
31
31
 
32
- A multi-select draft keeps its selected labels while the user opens or edits the custom answer, so its submitted item may carry both `selected` and `custom`; a single-select custom answer remains exclusive. Question detail reuses the assistant-output `MarkdownText` primitive, including its GFM rendering and untrusted-content policy. The capped card keeps its title, navigation, and submission actions fixed while long detail and choices share an internal scroll region. "Skip this question" retains other drafts and emits the existing blank `{ selected: [] }` result for that item, while close rejects the whole wait as `ASK_CANCELLED`.
32
+ A multi-select draft keeps its selected labels while the user opens or edits the custom answer, so its submitted item may carry both `selected` and `custom`; a single-select custom answer remains exclusive. Question detail reuses the assistant-output `MarkdownText` primitive, including its GFM rendering and untrusted-content policy. The capped card keeps its title, navigation, and submission actions fixed while long detail and choices share an internal scroll region. "Skip" retains other drafts and emits the existing blank `{ selected: [] }` result for that item, while close rejects the whole wait as `ASK_CANCELLED`.
33
33
 
34
34
  ### The plan-review card
35
35
 
36
- A `plan-review` intent — set by `dsh-plan-mode` on the `exit_plan_mode` review — renders the waiting-approval card layout: a `Plan review` strip, the plan as the scrolling markdown body, and one decision row of `Chat about it` / `Refuse` / `Approve`. Approve and Refuse answer with the asker's own option labels; `Chat about it` rejects the wait as `ASK_CANCELLED`, returning the composer so the user can say what they want instead.
36
+ A `plan-review` intent — set by `dsh-plan-mode` on the `exit_plan_mode` review — renders a compact approval card: a `Plan review` strip with a `View full plan` link, the plan title and a two-line plain-text summary, and `Request changes` / `Approve` actions. The complete plan opens in the sidebar through the link or its permanent Chat card. Approve answers with the asker's approval label; `Request changes` rejects the wait as `ASK_CANCELLED`, returning the composer for the user's feedback without submitting an approval. The card has no separate refusal button.
37
37
 
38
38
  ### Failure and recovery
39
39
 
40
- The generic question flow keeps its current page, selected labels, custom text, and explicit skips in a non-persisted Slot store scoped to the owning Session and keyed by the pending request's local render identity. Switching from Session A to B remounts the strict composer entry, but returning to A reuses A's store and restores the unfinished draft. A different request identity reads an empty draft and replaces the previous value on its first edit; a successful answer or cancellation clears the matching value. The host remains authoritative for whether the request is pending.
40
+ The generic question flow keeps its current page, selected labels, custom text, and explicit skips in a non-persisted Slot store scoped to the owning Session generation and keyed by the pending request's local render identity. Switching from Session A to B retires A when no other reference owns it, so returning to A starts an empty question draft; another reference that keeps A's generation alive also keeps that draft. A different request identity reads an empty draft and replaces the previous value on its first edit; a successful answer or cancellation clears the matching value. The host remains authoritative for whether the request is pending.
41
41
 
42
42
  -----
43
43
 
@@ -51,12 +51,14 @@ The package is one ownership rule: rendering a question is a host UI capability,
51
51
 
52
52
  ### Intent surface election
53
53
 
54
- The card claims a request only when it can send every answer that request allows: one question, the intent declared, the plan present as `detail`, the named approve label offered, and a binary single choice (at most one option besides approve, not multi-select). Anything else stays on the generic flow, which can express it. An intent changes the layout, never which answers are reachable.
54
+ The card accepts one question declaring the intent, carrying the plan as `detail`, and offering the named approve label, with at most one alternative and no multi-select. Its secondary action returns to the composer for change requests. Larger choices and multi-select questions remain in the generic flow.
55
55
 
56
56
  ### Copy and locale
57
57
 
58
58
  Composer chrome copy (pager, buttons, placeholders, validation feedback) is bilingual: the plugin registers zh/en dictionaries under the `question` namespace of `dsh-client-locale` and hands the entry its bound translator plus the locale snapshot source through the inject face, so a locale switch re-renders a mounted composer. Question and option text arrives from the model and renders verbatim; carrier failure messages also display untranslated.
59
59
 
60
+ A plan review exposes `conversation.plan-review.actions` with its request key, full text, and optional invocation identity. The plan plugin opens logged plans from history and unlogged reviews as temporary sidebar previews. Opening a document does not answer or dismiss the review.
61
+
60
62
  </details>
61
63
 
62
64
  -----
package/README.zh.md CHANGED
@@ -9,7 +9,7 @@ kind: "package-reference"
9
9
 
10
10
  ## 概述
11
11
 
12
- 当 agent 在 Web 客户端中提问时,本包会用交互式提问界面接管聊天编辑器。用户可以在问题之间导航、选择一个或多个选项、输入自定义答案、跳过问题,并提交一批结构化答案。选择单选项后会立即前进,而草稿会在当前页面的生命周期内跨 Session 导航保留。若唯一的问题声明了受支持的呈现意图,则可使用专用界面,包括带 `Chat about it`、`Refuse` 和 `Approve` 操作的 plan-review 卡片。
12
+ 当 agent(智能体)在 Web 客户端中提问时,本包会用交互式提问界面接管聊天编辑器。用户可以在问题之间导航、选择一个或多个选项、输入自定义答案、跳过问题,并提交一批结构化答案。选择单选项后会立即前进,而草稿会在当前页面的生命周期内跨会话导航保留。若唯一的问题声明了受支持的呈现意图,则可使用专用界面,包括带 `Request changes` 和 `Approve` 操作的 plan-review 卡片。
13
13
 
14
14
  ## 目录
15
15
 
@@ -29,15 +29,15 @@ kind: "package-reference"
29
29
 
30
30
  ### 作答
31
31
 
32
- 用户打开或编辑自定义答案时,多选题草稿会保留已选中的标签,因此提交项可以同时携带 `selected` 与 `custom`;单选题的自定义答案仍保持互斥。问题详情复用助手输出的 `MarkdownText` 原语,包括其 GFM 渲染与不受信任内容策略。限高卡片保持标题、导航与提交动作固定,超长的详情与选项共享内部滚动区。「跳过此问题」会保留其他草稿,并为该项发出既有的空 `{ selected: [] }` 结果;关闭则以 `ASK_CANCELLED` 拒绝整个等待。
32
+ 用户打开或编辑自定义答案时,多选题草稿会保留已选中的标签,因此提交项可以同时携带 `selected` 与 `custom`;单选题的自定义答案仍保持互斥。问题详情复用助手输出的 `MarkdownText` 原语,包括其 GFM 渲染与不受信任内容策略。限高卡片保持标题、导航与提交动作固定,超长的详情与选项共享内部滚动区。「跳过」会保留其他草稿,并为该项发出既有的空 `{ selected: [] }` 结果;关闭则以 `ASK_CANCELLED` 拒绝整个等待。
33
33
 
34
34
  ### plan-review 卡片
35
35
 
36
- `plan-review` 意图——由 `dsh-plan-mode` 在 `exit_plan_mode` 审阅上设置——渲染等待审批卡片的布局:一条 `Plan review` 条带、计划作为可滚动的 markdown 主体,以及一行 `Chat about it` / `Refuse` / `Approve` 的决定操作。Approve 与 Refuse 用提问方自己的选项标签回答;`Chat about it` 以 `ASK_CANCELLED` 拒绝该等待,让编辑器归位,用户可以直接说出他想说的话。
36
+ `plan-review` 意图——由 `dsh-plan-mode` 在 `exit_plan_mode` 审阅上设置——渲染紧凑审批卡片:顶部为带“查看全文”链接的“计划待审”条带,正文显示计划标题和两行纯文本摘要,底部提供“要求修改”和“同意执行”。通过链接或聊天历史中的常驻卡片可在侧边栏阅读完整计划。“同意执行”用提问方的批准标签回答;“要求修改”以 `ASK_CANCELLED` 拒绝等待,让编辑器归位供用户输入反馈,不会提交批准。卡片不再单列拒绝按钮。
37
37
 
38
38
  ### 失败与恢复
39
39
 
40
- 通用提问流程把当前题号、已选标签、自定义文本和显式跳过状态保存在非持久化 Slot store 中;该 store 归属对应 Session,并以待处理请求的本地渲染标识为 key。从 Session A 切换到 B 会重新挂载严格 Session 级编辑器条目,但返回 A 时会复用 A 的 store 并恢复未完成草稿。不同的请求标识读取空草稿,并在首次编辑时替换旧值;成功回答或取消会清除相符的值。请求是否仍在等待由主机保持权威。
40
+ 通用提问流程把当前题号、已选标签、自定义文本和显式跳过状态保存在非持久化 slot 存储中;该存储归属对应 Session generation,并以待处理请求的本地渲染标识为键。从 Session A 切换到 B 时,如果没有其他引用持有 A,A 就会结束,因此返回 A 时会得到空的问题草稿;如果另一个引用继续持有 A 的 generation,该草稿也会保留。不同的请求标识读取空草稿,并在首次编辑时替换旧值;成功回答或取消会清除相符的值。请求是否仍在等待由主机保持权威。
41
41
 
42
42
  -----
43
43
 
@@ -49,14 +49,16 @@ kind: "package-reference"
49
49
 
50
50
  本包是一条归属规则:渲染提问是宿主的 UI 能力,拥有该工具则是 agent 的能力,因此 `tool-ask-user` 行属于需要它的各个 preset(以及没有 preset 的 TUI 组装)。
51
51
 
52
- ### 意图表面选举
52
+ ### 意图界面选择
53
53
 
54
- 卡片只在能够发出该请求允许的每一个答案时才接管:只有一个问题、声明了意图、计划以 `detail` 存在、提供了被指名的批准标签,且是二元单选(除批准外最多一个选项,且非多选)。其他任何情形都留在能够表达它的通用流程上。意图改变的只是布局,从不改变可达的答案。
54
+ 卡片接管声明了意图、以 `detail` 携带计划、提供了被指名的批准标签的单个问题,要求除批准外最多一个选项,且非多选。次要操作返回编辑器供用户提出修改要求。更多选项或多选问题仍由通用流程处理。
55
55
 
56
56
  ### 文案与 locale
57
57
 
58
58
  编辑器外框文案(翻页器、按钮、占位符、校验提示)是双语的:插件在 `dsh-client-locale` 的 `question` 命名空间下注册 zh/en 词典,并通过 inject face 把绑定的翻译函数和 locale 快照源交给该条目,因此切换语言会重新渲染已挂载的编辑器。问题与选项文本来自模型并原样渲染;载体失败消息也不经翻译直接显示。
59
59
 
60
+ 计划审批通过 `conversation.plan-review.actions` 插槽提供请求键、完整正文和可选的调用标识。计划插件从历史中打开已记录计划,并把没有调用标识的审批作为临时侧边栏预览打开。打开文档不会回答或关闭审批。
61
+
60
62
  </details>
61
63
 
62
64
  -----
@@ -67,9 +69,9 @@ kind: "package-reference"
67
69
  以下页面覆盖编辑器宿主、工具 seam 与 plan-mode 消费方。
68
70
 
69
71
  - [ui-conversation](../ui-conversation/README.zh.md)——拥有 `conversation.composer` 链的聊天界面。
70
- - [tool-ask-user](../../interaction/tool-ask-user/README.zh.md)——本 UI 所渲染其 schema 与答案的面向模型工具。
72
+ - [tool-ask-user](../../interaction/tool-ask-user/README.zh.md)——面向模型的工具;本 UI 会渲染其 schema 与答案。
71
73
  - [ui-plan](../ui-plan/README.zh.md)——设置 `plan-review` 意图的 plan-mode 界面。
72
- - [user-questions](../../interaction/user-questions/README.zh.md)——Host 侧提问 seam 及其 answerer waterfall。
74
+ - [user-questions](../../interaction/user-questions/README.zh.md)——Host 侧提问 seam 及其应答方 waterfall(瀑布式事件)。
73
75
 
74
76
  -----
75
77
 
@@ -89,7 +91,7 @@ kind: "package-reference"
89
91
 
90
92
  这些限制定义草稿持久性与编辑器归属;它们是当前包约束。
91
93
 
92
- - **未提交草稿的生命周期限于当前页面与 Session**:只要该 Session scope 仍留在页面内,Session 导航就会保留草稿;完整刷新页面、Session 被裁剪,或待处理请求以新的本地标识重新交付时,则从空草稿开始。store 从不把草稿写入主机、`localStorage` 或磁盘。
94
+ - **未提交草稿的生命周期限于当前页面与会话**:只要该会话作用域仍留在页面内,会话导航就会保留草稿;完整刷新页面、会话被裁剪,或待处理请求以新的本地标识重新交付时,则从空草稿开始。存储从不把草稿写入主机、`localStorage` 或磁盘。
93
95
  - **每次只有一个请求拥有编辑器**:后续待处理请求仍留在会话快照中,并在较早请求落定后显示。
94
96
 
95
97
  <a id="dev-note"></a>
@@ -102,4 +104,4 @@ kind: "package-reference"
102
104
 
103
105
  </details>
104
106
 
105
- **运行时不变式:** 不发布伴生入口。tool 与 slot 注册都是由各自 registry 持有和观察的 effect;Host pending table 通过公开 wire protocol 测试。
107
+ **运行时不变式:** 不发布伴生入口。工具与 slot 注册都是由各自注册表持有和观察的 effect;Host 待处理表通过公开的 wire protocol 测试。
package/lib/client.js CHANGED
@@ -21,16 +21,10 @@ window.__ModuleLoader__.load({
21
21
  * Narrow a request to a renderable plan review, or return undefined to leave it
22
22
  * to the generic question flow.
23
23
  *
24
- * The card is one decision over one plan, and it claims a request only when it
25
- * can send every answer that request allows — an intent changes the layout,
26
- * never which answers are reachable. So the batch must be a single question
27
- * that declares the intent, carries the plan as its detail, offers the approve
28
- * label the intent names, and is a binary single choice: at most one option
29
- * besides approve, and not multi-select. A third option or a multi-select batch
30
- * has answers two buttons cannot express, so the generic flow keeps it — as it
31
- * keeps any request whose intent the asker's own service would have rejected,
32
- * because the client sits downstream of a wire boundary and every request must
33
- * stay answerable.
24
+ * The card offers approval and a return to the composer for change requests.
25
+ * It accepts one question carrying the plan as detail and the named approve
26
+ * option, with at most one alternative and no multi-select. Larger choices
27
+ * remain in the generic question flow.
34
28
  *
35
29
  * @param questions - the request's whole question batch.
36
30
  * @returns The narrowed review, or undefined when the generic flow owns it.
@@ -50,6 +44,7 @@ window.__ModuleLoader__.load({
50
44
  id: question.id,
51
45
  question: question.question,
52
46
  plan: question.detail,
47
+ ...intent.callId === void 0 ? {} : { callId: intent.callId },
53
48
  approve,
54
49
  ...decline === void 0 ? {} : { decline }
55
50
  };
@@ -205,7 +200,7 @@ window.__ModuleLoader__.load({
205
200
  }
206
201
  //#endregion
207
202
  //#region \0dsh-css:/home/runner/work/deepseek-harness/deepseek-harness/packages/client/ui-user-questions/src/client/PlanReviewPanel.module.css.mjs
208
- const css$1 = ".LVzXQa_frame{padding:6px calc(var(--dsh-composer-side-clearance) + 16px) 10px;justify-content:center;display:flex}.LVzXQa_card{width:100%;max-width:var(--dsh-chat-content-width);border:1px solid var(--dsw-alias-state-warn-secondary);background:var(--dsw-specific-input-major);max-height:min(60vh,520px);box-shadow:var(--dsw-shadow-lv2);color:var(--dsw-alias-label-primary);--dsh-scrollbar-thumb:var(--dsw-alias-scrollbar-bg-l2);--dsh-scrollbar-thumb-hover:var(--dsw-alias-scrollbar-hover-l2);border-radius:20px;flex-direction:column;display:flex;overflow:hidden}.LVzXQa_card,.LVzXQa_card *{box-sizing:border-box}.LVzXQa_strip{background:var(--dsw-alias-state-warn-tertiary);color:var(--dsw-alias-state-warn-primary);flex-shrink:0;align-items:center;gap:8px;padding:10px 16px;font-size:13px;line-height:18px;display:flex}.LVzXQa_dot{corner-shape:round;background:var(--dsw-alias-state-warn-primary);border-radius:50%;width:8px;height:8px}.LVzXQa_body{overscroll-behavior:contain;flex:auto;min-height:0;padding:12px 16px 4px;font-size:14px;line-height:22px;overflow-y:auto}.LVzXQa_footer{flex-shrink:0;justify-content:space-between;align-items:center;gap:12px;padding:8px 16px 12px;display:flex}.LVzXQa_feedback{min-height:16px;color:var(--dsw-alias-state-error-primary);font-size:11px;line-height:16px}.LVzXQa_actions{flex-shrink:0;align-items:center;gap:8px;display:flex}.LVzXQa_discuss{color:var(--dsw-alias-label-secondary);gap:6px}.LVzXQa_discuss:hover:not(:disabled){color:var(--dsw-alias-label-primary)}@media (width<=720px){.LVzXQa_card{border-radius:16px}.LVzXQa_body{padding:10px 12px 4px}.LVzXQa_footer{align-items:flex-end;padding:8px 12px 10px}}";
203
+ const css$1 = ".LVzXQa_frame{padding:6px calc(var(--dsh-composer-side-clearance) + 16px) 10px;justify-content:center;display:flex}.LVzXQa_card{width:100%;max-width:var(--dsh-chat-content-width);--dsw-elevation-stroke-color:var(--dsw-alias-border-l2);background:var(--dsw-specific-input-major);box-shadow:var(--dsw-elevation-panel);color:var(--dsw-alias-label-primary);border:0;border-radius:20px;flex-direction:column;display:flex;overflow:hidden}.LVzXQa_card,.LVzXQa_card *{box-sizing:border-box}.LVzXQa_strip{background:var(--dsw-alias-state-warn-tertiary);color:var(--dsw-alias-state-warn-primary);flex-shrink:0;align-items:center;gap:8px;padding:12px 16px;font-size:14px;line-height:20px;display:flex}.LVzXQa_dot{corner-shape:round;background:var(--dsw-alias-state-warn-primary);border-radius:50%;width:8px;height:8px}.LVzXQa_footer{flex-shrink:0;justify-content:space-between;align-items:center;gap:12px;padding:8px 16px 12px;display:flex}.LVzXQa_feedback{min-height:16px;color:var(--dsw-alias-state-error-primary);font-size:11px;line-height:16px}.LVzXQa_actions{flex-shrink:0;align-items:center;gap:8px;display:flex}.LVzXQa_summary{min-width:0;padding:14px 16px 12px}.LVzXQa_title{text-overflow:ellipsis;white-space:nowrap;margin:0;font-size:15px;font-weight:500;line-height:22px;overflow:hidden}.LVzXQa_description{-webkit-line-clamp:2;color:var(--dsw-alias-label-secondary);-webkit-box-orient:vertical;margin:8px 0 0;font-size:14px;line-height:24px;display:-webkit-box;overflow:hidden}.LVzXQa_discuss{gap:6px}@media (width<=720px){.LVzXQa_card{border-radius:16px}.LVzXQa_footer{align-items:flex-end;padding:8px 12px 10px}}.LVzXQa_previewActions{align-items:center;margin-left:auto;display:flex}";
209
204
  const tagId$1 = "@deepseek-ai/dsh-client-ui-user-questions/PlanReviewPanel.module.css";
210
205
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$1) + "]") === null) {
211
206
  const tag = document.createElement("style");
@@ -216,14 +211,17 @@ window.__ModuleLoader__.load({
216
211
  }
217
212
  var PlanReviewPanel_module_css_default = {
218
213
  "actions": "LVzXQa_actions",
219
- "body": "LVzXQa_body",
220
214
  "card": "LVzXQa_card",
215
+ "description": "LVzXQa_description",
221
216
  "discuss": "LVzXQa_discuss",
222
217
  "dot": "LVzXQa_dot",
223
218
  "feedback": "LVzXQa_feedback",
224
219
  "footer": "LVzXQa_footer",
225
220
  "frame": "LVzXQa_frame",
226
- "strip": "LVzXQa_strip"
221
+ "previewActions": "LVzXQa_previewActions",
222
+ "strip": "LVzXQa_strip",
223
+ "summary": "LVzXQa_summary",
224
+ "title": "LVzXQa_title"
227
225
  };
228
226
  //#endregion
229
227
  //#region lib/types/client/PlanReviewPanel.js
@@ -238,19 +236,12 @@ window.__ModuleLoader__.load({
238
236
  return description === void 0 ? {} : { title: description };
239
237
  }
240
238
  /**
241
- * Render a plan review as a decision card.
239
+ * Render plan review controls; the submitted document opens in the sidebar.
242
240
  *
243
241
  * @param props - the question domain face, the narrowed plan review, and `t`.
244
242
  * @returns The plan-review takeover for this request.
245
243
  */
246
- function PlanReviewPanel({ pending, review, t }) {
247
- const markdownLabels = (0, react.useMemo)(() => ({
248
- code: {
249
- copyLabel: t("copy"),
250
- copiedLabel: t("copied")
251
- },
252
- footnotes: t("markdown.footnotes")
253
- }), [t]);
244
+ function PlanReviewPanel({ pending, review, t, renderSlot }) {
254
245
  const [busy, setBusy] = (0, react.useState)(false);
255
246
  const [error, setError] = (0, react.useState)(null);
256
247
  const settle = (send) => {
@@ -267,7 +258,14 @@ window.__ModuleLoader__.load({
267
258
  selected: [label]
268
259
  }] }));
269
260
  };
270
- const decline = review.decline;
261
+ const summary = (0, react.useMemo)(() => {
262
+ const title = (0, _deepseek_ai_dsh_client_ui_primitives.extractMarkdownPlainText)(review.plan, { mode: "first-line" });
263
+ const description = (0, _deepseek_ai_dsh_client_ui_primitives.extractMarkdownPlainText)(review.plan, { mode: "first-paragraph" });
264
+ return {
265
+ title,
266
+ description: description === title ? "" : description
267
+ };
268
+ }, [review.plan]);
271
269
  return (0, react_jsx_runtime.jsx)("div", {
272
270
  className: PlanReviewPanel_module_css_default.frame,
273
271
  "data-plan-review-key": pending.key,
@@ -277,15 +275,27 @@ window.__ModuleLoader__.load({
277
275
  children: [
278
276
  (0, react_jsx_runtime.jsxs)("div", {
279
277
  className: PlanReviewPanel_module_css_default.strip,
280
- children: [(0, react_jsx_runtime.jsx)("span", { className: PlanReviewPanel_module_css_default.dot }), t("plan.header")]
278
+ children: [
279
+ (0, react_jsx_runtime.jsx)("span", { className: PlanReviewPanel_module_css_default.dot }),
280
+ t("plan.header"),
281
+ (0, react_jsx_runtime.jsx)("div", {
282
+ className: PlanReviewPanel_module_css_default.previewActions,
283
+ children: renderSlot("conversation.plan-review.actions", {
284
+ review,
285
+ requestKey: pending.key
286
+ })
287
+ })
288
+ ]
281
289
  }),
282
- (0, react_jsx_runtime.jsx)("div", {
283
- className: PlanReviewPanel_module_css_default.body,
284
- "data-plan-review-scroll": true,
285
- children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.MarkdownText, {
286
- text: review.plan,
287
- labels: markdownLabels
288
- })
290
+ (0, react_jsx_runtime.jsxs)("div", {
291
+ className: PlanReviewPanel_module_css_default.summary,
292
+ children: [(0, react_jsx_runtime.jsx)("h3", {
293
+ className: PlanReviewPanel_module_css_default.title,
294
+ children: summary.title
295
+ }), summary.description !== "" && (0, react_jsx_runtime.jsx)("p", {
296
+ className: PlanReviewPanel_module_css_default.description,
297
+ children: summary.description
298
+ })]
289
299
  }),
290
300
  (0, react_jsx_runtime.jsxs)("div", {
291
301
  className: PlanReviewPanel_module_css_default.footer,
@@ -295,36 +305,24 @@ window.__ModuleLoader__.load({
295
305
  children: error
296
306
  }), (0, react_jsx_runtime.jsxs)("div", {
297
307
  className: PlanReviewPanel_module_css_default.actions,
298
- children: [
299
- (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
300
- variant: "ghost",
301
- className: PlanReviewPanel_module_css_default.discuss,
302
- icon: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconEditOutline16, { size: 14 }),
303
- disabled: busy,
304
- onClick: () => {
305
- settle(() => pending.cancel());
306
- },
307
- children: t("plan.discuss")
308
- }),
309
- decline !== void 0 && (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
310
- variant: "outline",
311
- ...tooltip(decline.description),
312
- disabled: busy,
313
- onClick: () => {
314
- decide(decline.label);
315
- },
316
- children: t("plan.decline")
317
- }),
318
- (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
319
- variant: "primary",
320
- ...tooltip(review.approve.description),
321
- disabled: busy,
322
- onClick: () => {
323
- decide(review.approve.label);
324
- },
325
- children: t("plan.approve")
326
- })
327
- ]
308
+ children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
309
+ variant: "outline",
310
+ className: PlanReviewPanel_module_css_default.discuss,
311
+ icon: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconEditOutline16, { size: 14 }),
312
+ disabled: busy,
313
+ onClick: () => {
314
+ settle(() => pending.cancel());
315
+ },
316
+ children: t("plan.discuss")
317
+ }), (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
318
+ variant: "primary",
319
+ ...tooltip(review.approve.description),
320
+ disabled: busy,
321
+ onClick: () => {
322
+ decide(review.approve.label);
323
+ },
324
+ children: t("plan.approve")
325
+ })]
328
326
  })]
329
327
  })
330
328
  ]
@@ -460,7 +458,8 @@ window.__ModuleLoader__.load({
460
458
  }, question.key) : (0, react_jsx_runtime.jsx)(PlanReviewPanel, {
461
459
  pending: question,
462
460
  review,
463
- t: props.t
461
+ t: props.t,
462
+ renderSlot: props.renderSlot
464
463
  }, question.key);
465
464
  }
466
465
  function QuestionFlow({ pending, t, useStore, actions }) {
@@ -799,12 +798,12 @@ window.__ModuleLoader__.load({
799
798
  "nav.cancel": "放弃整组问题",
800
799
  "option.recommended": "推荐",
801
800
  "custom.placeholder": "输入你的答案",
802
- "action.skip": "跳过本题",
801
+ "action.skip": "跳过",
803
802
  "action.next": "下一题",
804
803
  "plan.header": "计划待审",
805
- "plan.approve": "确认执行",
804
+ "plan.approve": "同意执行",
806
805
  "plan.decline": "拒绝",
807
- "plan.discuss": "去聊天里说"
806
+ "plan.discuss": "要求修改"
808
807
  };
809
808
  /** English dictionary, checked complete against the zh key set. */
810
809
  const en = {
@@ -817,12 +816,12 @@ window.__ModuleLoader__.load({
817
816
  "nav.cancel": "Dismiss all questions",
818
817
  "option.recommended": "Recommended",
819
818
  "custom.placeholder": "Type your answer",
820
- "action.skip": "Skip this question",
819
+ "action.skip": "Skip",
821
820
  "action.next": "Next",
822
821
  "plan.header": "Plan review",
823
822
  "plan.approve": "Approve",
824
823
  "plan.decline": "Refuse",
825
- "plan.discuss": "Chat about it"
824
+ "plan.discuss": "Request changes"
826
825
  };
827
826
  //#endregion
828
827
  //#region lib/types/client/index.js
@@ -875,7 +874,11 @@ window.__ModuleLoader__.load({
875
874
  name: "conversation.composer",
876
875
  select: ({ pendingInteraction }) => pendingInteraction instanceof PendingQuestion ? pendingInteraction : null,
877
876
  locale: NS,
878
- store: questionDraftStore
877
+ store: questionDraftStore,
878
+ children: { "conversation.plan-review.actions": {
879
+ kind: "list",
880
+ scope: "session"
881
+ } }
879
882
  }, QuestionComposer));
880
883
  ctx.remote.$on("user-questions/request", function(request, next) {
881
884
  return answerQuestion(ctx, this, request, next, registerPendingInteraction);
@@ -3,12 +3,12 @@ import type { PendingQuestion, PlanReview, QuestionComposerProps } from './contr
3
3
  export type PlanReviewPanelProps = {
4
4
  pending: PendingQuestion;
5
5
  review: PlanReview;
6
- } & Pick<QuestionComposerProps, 't'>;
6
+ } & Pick<QuestionComposerProps, 't' | 'renderSlot'>;
7
7
  /**
8
- * Render a plan review as a decision card.
8
+ * Render plan review controls; the submitted document opens in the sidebar.
9
9
  *
10
10
  * @param props - the question domain face, the narrowed plan review, and `t`.
11
11
  * @returns The plan-review takeover for this request.
12
12
  */
13
- export declare function PlanReviewPanel({ pending, review, t }: PlanReviewPanelProps): import("react").JSX.Element;
13
+ export declare function PlanReviewPanel({ pending, review, t, renderSlot }: PlanReviewPanelProps): import("react").JSX.Element;
14
14
  //# sourceMappingURL=PlanReviewPanel.d.ts.map
@@ -1,5 +1,6 @@
1
1
  /** Question composer props and one pending Remote waterfall response. */
2
- import type { PropsLocale, PropsRuntime, PropsStore } from '@deepseek-ai/dsh-client-ui-slots';
2
+ import type { PropsLocale, PropsRenderSlots, PropsRuntime, PropsStore } from '@deepseek-ai/dsh-client-ui-slots';
3
+ import type { ToolCallId } from '@deepseek-ai/dsh-llm/brand';
3
4
  import type { SessionId } from '@deepseek-ai/dsh-session/types';
4
5
  import type { AskUserQuestionAnswer, AskUserQuestionItem } from '@deepseek-ai/dsh-user-questions';
5
6
  import type { createQuestionDraftStore } from '../draft-store.ts';
@@ -9,6 +10,19 @@ declare module '@deepseek-ai/dsh-client-ui-session/client' {
9
10
  question: PendingQuestion;
10
11
  }
11
12
  }
13
+ declare module '@deepseek-ai/dsh-client-ui-slots' {
14
+ interface SlotMap {
15
+ /** Actions for the exact plan under review; approval remains with the question composer. */
16
+ 'conversation.plan-review.actions': {
17
+ kind: 'list';
18
+ scope: 'session';
19
+ owner: {
20
+ review: PlanReview;
21
+ requestKey: PendingQuestion['key'];
22
+ };
23
+ };
24
+ }
25
+ }
12
26
  /** One structured answer batch covering every question of the request. */
13
27
  export type QuestionAnswer = AskUserQuestionAnswer;
14
28
  /** One question of the request. */
@@ -29,6 +43,8 @@ export interface PlanReview {
29
43
  question: string;
30
44
  /** The plan markdown under review. */
31
45
  plan: string;
46
+ /** Logged tool invocation used to reopen this plan. */
47
+ callId?: ToolCallId;
32
48
  /** The option that approves the plan. */
33
49
  approve: QuestionOption;
34
50
  /** The option that declines it; absent when the asker offered no other option. */
@@ -38,16 +54,10 @@ export interface PlanReview {
38
54
  * Narrow a request to a renderable plan review, or return undefined to leave it
39
55
  * to the generic question flow.
40
56
  *
41
- * The card is one decision over one plan, and it claims a request only when it
42
- * can send every answer that request allows — an intent changes the layout,
43
- * never which answers are reachable. So the batch must be a single question
44
- * that declares the intent, carries the plan as its detail, offers the approve
45
- * label the intent names, and is a binary single choice: at most one option
46
- * besides approve, and not multi-select. A third option or a multi-select batch
47
- * has answers two buttons cannot express, so the generic flow keeps it — as it
48
- * keeps any request whose intent the asker's own service would have rejected,
49
- * because the client sits downstream of a wire boundary and every request must
50
- * stay answerable.
57
+ * The card offers approval and a return to the composer for change requests.
58
+ * It accepts one question carrying the plan as detail and the named approve
59
+ * option, with at most one alternative and no multi-select. Larger choices
60
+ * remain in the generic question flow.
51
61
  *
52
62
  * @param questions - the request's whole question batch.
53
63
  * @returns The narrowed review, or undefined when the generic flow owns it.
@@ -102,7 +112,7 @@ export type QuestionWait = PendingQuestion;
102
112
  * standard locale seat; the carrier plus the domain face above carry the
103
113
  * whole behavior surface.
104
114
  */
105
- export type QuestionComposerProps = PropsRuntime<'conversation.composer'> & PropsStore<ReturnType<typeof createQuestionDraftStore>> & {
115
+ export type QuestionComposerProps = PropsRuntime<'conversation.composer'> & PropsStore<ReturnType<typeof createQuestionDraftStore>> & PropsRenderSlots<'conversation.plan-review.actions'> & {
106
116
  matched: QuestionWait;
107
117
  } & PropsLocale<'question'>;
108
118
  export {};
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@deepseek-ai/dsh-client-ui-user-questions",
3
3
  "description": "Web ask_user_question composer takeover and plan-review presentation UI",
4
- "version": "0.1.5-rc.2",
4
+ "version": "0.1.6-alpha.2",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -46,23 +46,24 @@
46
46
  "@types/react": "~18.3.1",
47
47
  "react": "^18.2.0",
48
48
  "clsx": "^2.0.0",
49
+ "@deepseek-ai/dsh-agent": "^0.1.6-alpha.2",
49
50
  "@deepseek-ai/cordis": "^4.0.2",
50
- "@deepseek-ai/dsh-agent": "^0.1.5-rc.2",
51
- "@deepseek-ai/dsh-api-remotes": "^0.1.5-rc.2",
52
- "@deepseek-ai/dsh-api-session-controller": "^0.1.5-rc.2",
53
- "@deepseek-ai/dsh-client-locale": "^0.1.5-rc.2",
54
- "@deepseek-ai/dsh-client-store": "^0.1.5-rc.2",
55
- "@deepseek-ai/dsh-system-prompt": "^0.1.5-rc.2",
56
- "@deepseek-ai/dsh-tools": "^0.1.5-rc.2",
57
- "@deepseek-ai/dsh-user-questions": "^0.1.5-rc.2",
58
- "@deepseek-ai/dsh-client-ui-primitives": "^0.1.5-rc.2",
59
- "@deepseek-ai/dsh-client-ui-slots": "^0.1.5-rc.2",
60
- "@deepseek-ai/dsh-client-ui-renderer": "^0.1.5-rc.2",
61
- "@deepseek-ai/dsh-client-ui-session": "^0.1.5-rc.2",
62
- "@deepseek-ai/dsh-typert-protocol": "^0.1.5-rc.2",
63
- "@deepseek-ai/dsh-client-connection": "^0.1.5-rc.2",
64
- "@deepseek-ai/dsh-session": "^0.1.5-rc.2",
65
- "@deepseek-ai/dsh-client-ui-conversation": "^0.1.5-rc.2"
51
+ "@deepseek-ai/dsh-api-remotes": "^0.1.6-alpha.2",
52
+ "@deepseek-ai/dsh-api-session-controller": "^0.1.6-alpha.2",
53
+ "@deepseek-ai/dsh-client-locale": "^0.1.6-alpha.2",
54
+ "@deepseek-ai/dsh-client-store": "^0.1.6-alpha.2",
55
+ "@deepseek-ai/dsh-system-prompt": "^0.1.6-alpha.2",
56
+ "@deepseek-ai/dsh-tools": "^0.1.6-alpha.2",
57
+ "@deepseek-ai/dsh-client-ui-primitives": "^0.1.6-alpha.2",
58
+ "@deepseek-ai/dsh-user-questions": "^0.1.6-alpha.2",
59
+ "@deepseek-ai/dsh-client-ui-slots": "^0.1.6-alpha.2",
60
+ "@deepseek-ai/dsh-client-ui-renderer": "^0.1.6-alpha.2",
61
+ "@deepseek-ai/dsh-client-ui-conversation": "^0.1.6-alpha.2",
62
+ "@deepseek-ai/dsh-llm": "^0.1.6-alpha.2",
63
+ "@deepseek-ai/dsh-client-ui-session": "^0.1.6-alpha.2",
64
+ "@deepseek-ai/dsh-session": "^0.1.6-alpha.2",
65
+ "@deepseek-ai/dsh-typert-protocol": "^0.1.6-alpha.2",
66
+ "@deepseek-ai/dsh-client-connection": "^0.1.6-alpha.2"
66
67
  },
67
68
  "files": [
68
69
  "lib/index.js",