@deepseek-ai/dsh-client-ui-plan 0.1.6-alpha.2 → 0.1.7-alpha.1

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-plan/README.md
5
- README.md: f7e7aede2c76717228d101153e4d3d2f8d3f88c4
6
- README.zh.md: 9ab37a25dd7cb406b42bf5c531445357d6725e2e
5
+ README.md: e9e38251ff69ed601e486b4c1ce351b236e7bcb7
6
+ README.zh.md: 91fe232d27007fea8264e98c895ca2788585710c
package/README.md CHANGED
@@ -29,13 +29,13 @@ Mount this plugin alongside `ui-conversation` and `dsh-plan-mode`; the chip then
29
29
 
30
30
  ### What the chip shows
31
31
 
32
- While the effective target is plan mode, the seat renders the warn-colored "Plan ×" status button, which executes `/plan off`. Otherwise the seat stays empty: a host without plan mode, or a Draft with no session, shows nothing. While plan mode is the effective target, the composer textarea's placeholder switches to the plan-task hint — "describe your task to generate plan" — unless the owning surface supplies its own placeholder.
32
+ While the effective target is plan mode, the seat renders the blue "Plan" status button — the plan glyph ahead of the label, swapped for a circled cross while the enabled button is hovered or keyboard-focused — which executes `/plan off`. Otherwise the seat stays empty: a host without plan mode, or a Draft with no session, shows nothing. While plan mode is the effective target, the composer textarea's placeholder switches to the plan-task hint — "describe your task to generate plan" — unless the owning surface supplies its own placeholder.
33
33
 
34
34
  ### Reading submitted plans
35
35
 
36
- When a Turn ends, each submitted plan appears in its final artifact area, using the file-delivery card treatment with a Markdown icon, title, and Open action. A pending plan opens automatically once per submission in the current browser session. Closing it stays effective across review remounts; a new submission opens its own plan. Historical cards open only when clicked. Use the card or review strip’s View full plan link to read and copy the complete Markdown. Plan tabs show a document-and-pen icon. Different submissions retain separate tabs; the review buttons alone decide whether implementation may begin.
36
+ When a Turn ends, each submitted plan appears in its final artifact area, using the file-delivery card treatment with a Markdown icon, title, and Open action. A pending plan opens automatically once per submission in the current browser session. Closing it stays effective across review remounts; a new submission opens its own plan. Historical cards open only when clicked. Use the card or review strip’s View full plan link to read the complete Markdown. Plan tabs show a plain text file icon. Different submissions retain separate tabs; the review buttons alone decide whether implementation may begin.
37
37
 
38
- A review without a logged invocation also opens automatically. Its complete text lives only in the tab’s navigation memory, and the pending review card can reopen it. Reloading the page loses that text; an expired preview directs the user back to a pending review.
38
+ A review without a logged invocation also opens automatically. Its complete text lives only in the tab’s navigation memory, and the pending review card can reopen it. Reloading the page loses that text; an expired preview directs the user back to a pending review. Plans opened from an embedded child conversation use the visible sidebar while retaining the child’s address for document reads.
39
39
 
40
40
  ### Failures
41
41
 
@@ -49,9 +49,11 @@ Admission failures (`matched: false`, business errors, transport faults) surface
49
49
  <details>
50
50
  <summary>Implementation internals — click to expand</summary>
51
51
 
52
- The chip occupies the conversation-declared `conversation.input.plan` single seat; the node half is an empty apply (the roster row). Reads ride the generic projection pair through the standard-kit `useProjection`: the effective target is `pending ? !active : active` — a folded host value, not client optimism, so an arriving frame corrects the chip either way. The seat's injected face carries one verb, `exitPlanMode`, which executes `/plan off` through `ctx.remote.commands.execute` and maps admission failures to an inline error line. The placeholder and hint text live in ui-conversation's `conversation` locale namespace and are shared verbatim with the claimed `/plan` command hint. The accessible description is "Plan mode on, press to turn off".
52
+ The chip occupies the conversation-declared `conversation.input.plan` single seat; the node half is an empty apply (the roster row). Reads ride the generic projection pair through the standard-kit `useProjection`: the effective target is `pending ? !active : active` — a folded host value, not client optimism, so an arriving frame corrects the chip either way. The seat's injected face carries one verb, `exitPlanMode`, which executes `/plan off` through `ctx.remote.commands.execute` and maps admission failures to an inline error line. The placeholder and hint text live in ui-conversation's `conversation` locale namespace and are shared verbatim with the claimed `/plan` command hint.
53
53
 
54
- Plan cards derive from native `tool/call` or PTC dispatch arguments through a Conversation Definition, with each invocation’s resolved Turn location. They contribute to the additive `conversation.chat.turnTail` list alongside file deliveries. The plan resource address identifies the invocation and its complete ordinary or direct-parent subagent Session address; its provider reads existing Session history, including older pages, without storing document text in sidebar layout. The question plugin owns the review action slot and supplies its request key, complete text, and optional invocation identity. The [decision](../../../.agents/notes/implemented/feature/2026-09-17-persistent-plan-cards.md) explains why review lifetime and document lifetime remain separate.
54
+ Plan cards derive from native `tool/call` or PTC dispatch arguments through a Conversation Definition, with each invocation’s resolved Turn location. They contribute to the additive `conversation.chat.turnTail` list alongside file deliveries. The plan resource address identifies the invocation and its complete ordinary or direct-parent subagent Session address; its provider reads existing Session history, including older pages, without storing document text in sidebar layout. The question plugin owns the review action slot and supplies its request key, complete text, and optional invocation identity. The automatic open reads `ctx.sidebarRight.mounted` through a bound hook and runs once a seat is on screen: a review arriving while a global panel is active mounts ahead of the returning seat in the same commit, before that seat binds. The [decision](../../../.agents/notes/implemented/feature/2026-09-17-persistent-plan-cards.md) explains why review lifetime and document lifetime remain separate. Subagent plan addresses also preserve unknown mode so history reads can resolve the child descriptor.
55
+
56
+ The framework-bound `usePlans(turn)` exposes only submitted-plan data for that Turn. Chat indexes membership on Node updates and orders this collection when read; card rendering neither scans the transcript nor subscribes to other Turns or Node kinds.
55
57
 
56
58
  </details>
57
59
 
package/README.zh.md CHANGED
@@ -25,17 +25,17 @@ kind: "package-reference"
25
25
  <a id="use-this-package"></a>
26
26
  ## 使用本包
27
27
 
28
- 与 `ui-conversation` 及 `dsh-plan-mode` 一起挂载本插件;plan 模式激活时,徽章随即占据 composer 的 plan 座位(访问模式控件右侧)。经 `/plan` 命令路径进入 plan 模式——从 composer 的 `+` Command 菜单选择 Plan,或键入 `/plan`——再用徽章将其关闭。
28
+ 与 `ui-conversation` 及 `dsh-plan-mode` 一起挂载本插件;plan 模式激活时,徽章随即占据 composer 的 plan 座位(访问模式控件右侧)。经 `/plan` 命令路径进入 plan 模式——从 composer 的 `+` Command 菜单选择「计划」,或键入 `/plan`——再用徽章将其关闭。
29
29
 
30
30
  ### 徽章显示什么
31
31
 
32
- 当有效目标为 plan 模式时,该座位渲染 warn 色「Plan ×」状态按钮,执行 `/plan off`。否则座位保持为空:未组合 plan-mode 的宿主,或尚无会话的 Draft,都不显示任何内容。plan 模式为有效目标期间,composer 文本框的 placeholder 切换为 plan 任务提示——「describe your task to generate plan」——除非所属 surface 提供自己的 placeholder。
32
+ 当有效目标为 plan 模式时,该座位渲染蓝色「计划」状态按钮——标签前是计划图标,按钮可用且被悬停或键盘聚焦时换成圆形 ×——点击执行 `/plan off`。否则座位保持为空:未组合 plan-mode 的宿主,或尚无会话的 Draft,都不显示任何内容。plan 模式为有效目标期间,composer 文本框的 placeholder 切换为 plan 任务提示——「describe your task to generate plan」——除非所属 surface 提供自己的 placeholder。
33
33
 
34
34
  ### 查看已提交的计划
35
35
 
36
- 回合结束后,每次提交的计划都会显示在该回合末尾的产物区域,并采用文件产物卡片的样式,包含 Markdown 图标、标题和“打开”操作。在当前浏览器会话中,每份待审计划自动打开一次;关闭后,重新挂载审批组件不会再次展开,新提交会打开其对应计划。历史卡片仅在点击后打开。通过卡片或审批条带上的“查看全文”链接可阅读和复制完整 Markdown。计划标签页显示文档加笔的图标。不同提交保留独立标签页;是否开始实施仍由审批按钮决定。
36
+ 回合结束后,每次提交的计划都会显示在该回合末尾的产物区域,并采用文件产物卡片的样式,包含 Markdown 图标、标题和“打开”操作。在当前浏览器会话中,每份待审计划自动打开一次;关闭后,重新挂载审批组件不会再次展开,新提交会打开其对应计划。历史卡片仅在点击后打开。通过卡片或审批条带上的“查看全文”链接可阅读完整 Markdown。计划标签页显示纯文本文件图标。不同提交保留独立标签页;是否开始实施仍由审批按钮决定。
37
37
 
38
- 没有已记录调用标识的审批也会自动打开。完整正文只保存在标签页的导航内存中,待处理审批卡片可以重新打开它。刷新页面会丢失该正文;失效的预览会提示用户返回待处理审批。
38
+ 没有已记录调用标识的审批也会自动打开。完整正文只保存在标签页的导航内存中,待处理审批卡片可以重新打开它。刷新页面会丢失该正文;失效的预览会提示用户返回待处理审批。在嵌入式子对话中打开计划时,预览使用当前可见的侧栏,读取文档仍保留子会话地址。
39
39
 
40
40
  ### 失败
41
41
 
@@ -49,9 +49,11 @@ kind: "package-reference"
49
49
  <details>
50
50
  <summary>实现细节——点击展开</summary>
51
51
 
52
- 徽章占据 conversation 声明的 `conversation.input.plan` 单实例座位;node 半部是空 apply(roster 行)。读取经 standard-kit 的 `useProjection` 走通用投影对:有效目标是 `pending ? !active : active`——折叠的宿主值而非客户端乐观态,因此到达的帧无论哪个方向都会纠正徽章。座位注入面携带一个动词 `exitPlanMode`,经 `ctx.remote.commands.execute` 执行 `/plan off`,并把准入失败映射为一行内联错误。placeholder 与提示文案位于 ui-conversation 的 `conversation` locale 命名空间,与已认领 `/plan` 命令的提示逐字共用。无障碍描述是「Plan mode on, press to turn off」。
52
+ 徽章占据 conversation 声明的 `conversation.input.plan` 单实例座位;node 半部是空 apply(roster 行)。读取经 standard-kit 的 `useProjection` 走通用投影对:有效目标是 `pending ? !active : active`——折叠的宿主值而非客户端乐观态,因此到达的帧无论哪个方向都会纠正徽章。座位注入面携带一个动词 `exitPlanMode`,经 `ctx.remote.commands.execute` 执行 `/plan off`,并把准入失败映射为一行内联错误。placeholder 与提示文案位于 ui-conversation 的 `conversation` locale 命名空间,与已认领 `/plan` 命令的提示逐字共用。
53
53
 
54
- 计划卡片通过 Conversation Definition 从原生 `tool/call` 或 PTC dispatch 参数派生,并使用每次调用已解析的回合位置。它们与文件产物一起贡献到可追加的 `conversation.chat.turnTail` 列表。计划资源地址标识调用及完整的普通会话地址或子会话直接父级地址;provider 读取已有会话历史及较早分页,不把正文存入侧边栏布局。提问插件拥有审批操作插槽,并提供请求键、完整正文和可选的调用标识。[决策记录](../../../.agents/notes/implemented/feature/2026-09-17-persistent-plan-cards.zh.md)说明审批与文档为何保持独立生命周期。
54
+ 计划卡片通过 Conversation Definition 从原生 `tool/call` 或 PTC dispatch 参数派生,并使用每次调用已解析的回合位置。它们与文件产物一起贡献到可追加的 `conversation.chat.turnTail` 列表。计划资源地址标识调用及完整的普通会话地址或子会话直接父级地址;provider 读取已有会话历史及较早分页,不把正文存入侧边栏布局。提问插件拥有审批操作插槽,并提供请求键、完整正文和可选的调用标识。自动打开通过绑定的 hook 读取 `ctx.sidebarRight.mounted`,在座位出现在屏幕上后才执行:全局面板打开期间到达的审批,会在返回时与座位在同一次提交里先挂载、早于座位完成绑定。[决策记录](../../../.agents/notes/implemented/feature/2026-09-17-persistent-plan-cards.zh.md)说明审批与文档为何保持独立生命周期。 子代理计划地址也保留未知模式,以便历史读取解析子 descriptor。
55
+
56
+ 框架绑定的 `usePlans(turn)` 只提供该回合的已提交计划数据。Chat 随节点更新维护成员索引,并在读取时排列该集合;卡片渲染不扫描整段对话,也不订阅其他回合或节点类型。
55
57
 
56
58
  </details>
57
59
 
package/lib/client.js CHANGED
@@ -7,8 +7,8 @@ window.__ModuleLoader__.load({
7
7
  let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
8
8
  let react_jsx_runtime = require("react/jsx-runtime");
9
9
  let react = require("react");
10
- let _deepseek_ai_dsh_client_store = require("@deepseek-ai/dsh-client-store");
11
10
  require("@deepseek-ai/cordis");
11
+ let _deepseek_ai_dsh_client_store = require("@deepseek-ai/dsh-client-store");
12
12
  //#region ../../util/crypto/src/index.ts
13
13
  /**
14
14
  * Random v4 UUID, minted from `crypto.getRandomValues`.
@@ -23,7 +23,7 @@ window.__ModuleLoader__.load({
23
23
  }
24
24
  //#endregion
25
25
  //#region \0dsh-css:/home/runner/work/deepseek-harness/deepseek-harness/packages/client/ui-plan/src/client/PlanPreview.module.css.mjs
26
- const css$1 = ".k74WwW_cards{flex-direction:column;gap:10px;display:flex}.k74WwW_card{--plan-card-fill:var(--dsw-static-neutral-50);--plan-card-hover:var(--dsw-static-neutral-100);box-sizing:border-box;border:.5px solid var(--dsw-alias-border-l1);background:var(--plan-card-fill);width:100%;min-width:0;height:60px;color:var(--dsw-alias-label-primary);font:inherit;text-align:left;cursor:pointer;border-radius:18px;align-items:center;gap:10px;margin:0;padding:8px 10px;transition:background-color .12s;display:flex}body[data-ds-dark-theme] .k74WwW_card{--plan-card-fill:var(--dsw-static-neutral-850);--plan-card-hover:var(--dsw-static-neutral-800)}.k74WwW_card:hover{background:var(--plan-card-hover)}.k74WwW_card:focus-visible,.k74WwW_iconButton:focus-visible{outline:2px solid var(--dsw-alias-label-primary);outline-offset:3px}.k74WwW_cardIcon{box-sizing:border-box;border:.5px solid var(--dsw-alias-border-l1);background:var(--plan-card-fill);border-radius:10px;flex:none;place-items:center;width:40px;height:40px;display:grid}.k74WwW_cardDetails{flex-direction:column;flex:1;gap:2px;min-width:0;display:flex}.k74WwW_cardTitle{text-overflow:ellipsis;white-space:nowrap;font-size:13px;font-weight:500;line-height:20px;overflow:hidden}.k74WwW_cardDescription{text-overflow:ellipsis;white-space:nowrap;color:var(--dsw-alias-label-tertiary);font-size:10px;line-height:16px;overflow:hidden}.k74WwW_cardOpen{box-sizing:border-box;border:.5px solid var(--dsw-alias-border-l3);background:var(--dsw-alias-button-floating-fill);border-radius:10px;flex:none;align-items:center;height:28px;padding:4px 8px;font-size:12px;line-height:18px;display:inline-flex}.k74WwW_reviewLink{color:var(--dsw-alias-label-secondary);font:inherit;cursor:pointer;background:0 0;border:0;align-items:center;gap:4px;padding:0;display:inline-flex}.k74WwW_reviewLink:hover{color:var(--dsw-alias-label-primary)}.k74WwW_reviewLink:focus-visible{outline:2px solid var(--dsw-alias-label-primary);outline-offset:3px}.k74WwW_iconButton{width:28px;height:28px;color:inherit;cursor:pointer;background:0 0;border:0;border-radius:6px;justify-content:center;align-items:center;display:flex}.k74WwW_iconButton:hover{background:var(--dsw-alias-interactive-bg-hover)}.k74WwW_preview{box-sizing:border-box;height:100%;padding:20px 24px 40px;position:relative;overflow:auto}.k74WwW_toolbar{justify-content:flex-end;margin-bottom:8px;display:flex}.k74WwW_document{color:var(--dsw-alias-label-primary);overflow-wrap:anywhere;font-size:14px;line-height:1.75}.k74WwW_message{color:var(--dsw-alias-label-secondary);padding:24px;font-size:14px}.k74WwW_titleIcon{flex:none;display:inline-flex}@media (width<=767px){.k74WwW_preview{padding:16px 18px 32px}}";
26
+ const css$1 = ".k74WwW_cards{flex-direction:column;gap:10px;display:flex}.k74WwW_card{--plan-card-fill:var(--dsw-static-neutral-50);--plan-card-hover:var(--dsw-static-neutral-100);box-sizing:border-box;border:.5px solid var(--dsw-alias-border-l1);background:var(--plan-card-fill);width:100%;min-width:0;height:60px;color:var(--dsw-alias-label-primary);font:inherit;text-align:left;cursor:pointer;border-radius:18px;align-items:center;gap:10px;margin:0;padding:8px 10px;transition:background-color .12s;display:flex}body[data-ds-dark-theme] .k74WwW_card{--plan-card-fill:var(--dsw-static-neutral-850);--plan-card-hover:var(--dsw-static-neutral-800)}.k74WwW_card:hover{background:var(--plan-card-hover)}.k74WwW_card:focus-visible{outline:2px solid var(--dsw-alias-label-primary);outline-offset:3px}.k74WwW_cardIcon{box-sizing:border-box;border:.5px solid var(--dsw-alias-border-l1);background:var(--plan-card-fill);border-radius:10px;flex:none;place-items:center;width:40px;height:40px;display:grid}.k74WwW_cardDetails{flex-direction:column;flex:1;gap:2px;min-width:0;display:flex}.k74WwW_cardTitle{text-overflow:ellipsis;white-space:nowrap;font-size:13px;font-weight:500;line-height:20px;overflow:hidden}.k74WwW_cardDescription{text-overflow:ellipsis;white-space:nowrap;color:var(--dsw-alias-label-tertiary);font-size:10px;line-height:16px;overflow:hidden}.k74WwW_cardOpen{box-sizing:border-box;border:.5px solid var(--dsw-alias-border-l3);background:var(--dsw-alias-button-floating-fill);border-radius:10px;flex:none;align-items:center;height:28px;padding:4px 8px;font-size:12px;line-height:18px;display:inline-flex}.k74WwW_reviewLink{color:var(--dsw-alias-label-secondary);font:inherit;cursor:pointer;background:0 0;border:0;align-items:center;gap:4px;padding:0;display:inline-flex}.k74WwW_reviewLink:hover{color:var(--dsw-alias-label-primary)}.k74WwW_reviewLink:focus-visible{outline:2px solid var(--dsw-alias-label-primary);outline-offset:3px}.k74WwW_preview{box-sizing:border-box;height:100%;padding:20px 24px 40px;position:relative;overflow:auto}.k74WwW_document{color:var(--dsw-alias-label-primary);overflow-wrap:anywhere;font-size:14px;line-height:1.75}.k74WwW_message{color:var(--dsw-alias-label-secondary);padding:24px;font-size:14px}.k74WwW_titleIcon{flex:none}@media (width<=767px){.k74WwW_preview{padding:16px 18px 32px}}";
27
27
  const tagId$1 = "@deepseek-ai/dsh-client-ui-plan/PlanPreview.module.css";
28
28
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$1) + "]") === null) {
29
29
  const tag = document.createElement("style");
@@ -41,12 +41,10 @@ window.__ModuleLoader__.load({
41
41
  "cardTitle": "k74WwW_cardTitle",
42
42
  "cards": "k74WwW_cards",
43
43
  "document": "k74WwW_document",
44
- "iconButton": "k74WwW_iconButton",
45
44
  "message": "k74WwW_message",
46
45
  "preview": "k74WwW_preview",
47
46
  "reviewLink": "k74WwW_reviewLink",
48
- "titleIcon": "k74WwW_titleIcon",
49
- "toolbar": "k74WwW_toolbar"
47
+ "titleIcon": "k74WwW_titleIcon"
50
48
  };
51
49
  //#endregion
52
50
  //#region lib/types/client/PlanCard.js
@@ -56,13 +54,13 @@ window.__ModuleLoader__.load({
56
54
  * @param props - Logged plan, localized copy, and Session-bound navigation.
57
55
  * @returns keyboard-accessible plan cards, or null for a Turn without plans.
58
56
  */
59
- function PlanCards({ turn, useChat, openPlan, t }) {
60
- const plans = useChat((snapshot) => snapshot.nodes.values().filter((node) => node.kind === "submitted-plan" && (node.location.kind === "turn" || node.location.kind === "step") && node.location.turn.turn === turn.turn).sort((a, b) => a.anchorSeq - b.anchorSeq), _deepseek_ai_dsh_client_store.shallowEqual);
61
- if (plans.length === 0) return null;
57
+ function PlanCards({ turn, usePlans, openPlan, t }) {
58
+ const plans = usePlans(String(turn.turn));
59
+ if (plans === void 0 || plans.length === 0) return null;
62
60
  return (0, react_jsx_runtime.jsx)("div", {
63
61
  className: PlanPreview_module_css_default.cards,
64
62
  "data-plan-artifacts": true,
65
- children: plans.map(({ data: plan }) => (0, react_jsx_runtime.jsxs)("button", {
63
+ children: plans.map((plan) => (0, react_jsx_runtime.jsxs)("button", {
66
64
  type: "button",
67
65
  className: PlanPreview_module_css_default.card,
68
66
  "data-plan-card": plan.callId,
@@ -98,19 +96,26 @@ window.__ModuleLoader__.load({
98
96
  }
99
97
  /**
100
98
  * Open each pending plan automatically and retain a manual opener without answering it.
99
+ *
100
+ * The automatic open waits for a mounted Sidebar seat: a review that arrives
101
+ * while the Conversation is off screen mounts in the same commit as the seat,
102
+ * ahead of it, and the seat binds from its own effect. Reading the bound
103
+ * session through the hook opens once that binding exists.
101
104
  * @param props - Review identity, Session store, localized copy, and navigation.
102
105
  * @returns an opener for either logged or temporary plan text.
103
106
  */
104
- function PlanReviewOpen({ review, requestKey, openReview, t, useStore, actions }) {
107
+ function PlanReviewOpen({ review, requestKey, openReview, useSidebarMounted, t, useStore, actions }) {
105
108
  const identity = review.callId === void 0 ? `review:${requestKey}` : `call:${review.callId}`;
106
109
  const opened = useStore((state) => state.opened[identity] === true);
110
+ const mounted = useSidebarMounted((session) => session !== void 0);
107
111
  (0, react.useEffect)(() => {
108
- if (opened) return;
112
+ if (opened || !mounted) return;
109
113
  openReview(review, requestKey);
110
114
  actions.markOpened(identity);
111
115
  }, [
112
116
  identity,
113
117
  opened,
118
+ mounted,
114
119
  openReview,
115
120
  review,
116
121
  requestKey,
@@ -124,7 +129,7 @@ window.__ModuleLoader__.load({
124
129
  onClick: () => {
125
130
  openReview(review, requestKey);
126
131
  },
127
- children: [t("preview.full"), (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronRightOutline14, { size: 14 })]
132
+ children: [t("preview.full"), (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronRightOutlineRegular, { size: 14 })]
128
133
  });
129
134
  }
130
135
  //#endregion
@@ -166,7 +171,7 @@ window.__ModuleLoader__.load({
166
171
  //#region lib/types/client/PlanPreview.js
167
172
  /** Read-only Markdown viewer for logged plans and temporary review documents. */
168
173
  /**
169
- * Render the submitted plan with its complete Markdown and a copy action.
174
+ * Render the submitted plan with its complete Markdown.
170
175
  * @param props - Framework-bound tab identity, resource, and copy.
171
176
  * @returns the plan document or a localized loading/failure state.
172
177
  */
@@ -188,44 +193,33 @@ window.__ModuleLoader__.load({
188
193
  role: "status",
189
194
  children: [temporary ? t("preview.expired") : resource.status === "none" ? t("preview.unavailable") : resource.status === "failed" ? t("preview.failed") : t("preview.loading"), !temporary && resource.failure !== void 0 && (0, react_jsx_runtime.jsx)("p", { children: planFailureLine(t, resource.failure) })]
190
195
  });
191
- return (0, react_jsx_runtime.jsxs)("section", {
196
+ return (0, react_jsx_runtime.jsx)("section", {
192
197
  className: PlanPreview_module_css_default.preview,
193
198
  "data-plan-preview": "callId" in plan ? plan.callId : tab.tab.navigation.address,
194
199
  "aria-label": plan.title,
195
- children: [(0, react_jsx_runtime.jsx)("div", {
196
- className: PlanPreview_module_css_default.toolbar,
197
- children: (0, react_jsx_runtime.jsx)("button", {
198
- type: "button",
199
- className: PlanPreview_module_css_default.iconButton,
200
- "aria-label": t("copy"),
201
- onClick: () => {
202
- (0, _deepseek_ai_dsh_client_ui_primitives.writeClipboard)(plan.markdown);
203
- },
204
- children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCopyOutline16, {})
205
- })
206
- }), (0, react_jsx_runtime.jsx)("div", {
200
+ children: (0, react_jsx_runtime.jsx)("div", {
207
201
  className: PlanPreview_module_css_default.document,
208
202
  children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.MarkdownText, {
209
203
  text: plan.markdown,
210
204
  labels
211
205
  })
212
- })]
206
+ })
213
207
  });
214
208
  }
215
209
  /**
216
- * Display a plan icon and the heading in its tab after resource recovery.
210
+ * Display a plain file icon and the heading in its tab after resource recovery.
217
211
  * @param props - Framework-bound tab identity and resource reader.
218
- * @returns a decorative plan icon followed by the recovered title or initial localized label.
212
+ * @returns a decorative file icon followed by the recovered title or initial localized label.
219
213
  */
220
214
  function PlanTitle({ useTabInfo, useResource }) {
221
215
  const tab = useTabInfo();
222
216
  const resource = useResource(tab.tab.navigation.address);
223
217
  const params = tab.tab.navigation.params;
224
218
  const plan = isReviewPreviewAddress(tab.tab.navigation.address) ? params !== void 0 && "planReview" in params ? params.planReview : void 0 : resource.value;
225
- return (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsx)("span", {
226
- className: PlanPreview_module_css_default.titleIcon,
227
- "aria-hidden": "true",
228
- children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPlanOutline14, { size: 16 })
219
+ return (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.FileTypeIcon, {
220
+ kind: "other",
221
+ size: 16,
222
+ className: PlanPreview_module_css_default.titleIcon
229
223
  }), plan?.title ?? tab.tab.title] });
230
224
  }
231
225
  //#endregion
@@ -295,7 +289,7 @@ window.__ModuleLoader__.load({
295
289
  },
296
290
  callId: parts[1]
297
291
  };
298
- if (parts.length === 5 && parts[0] === "subagent" && (parts[3] === "one-shot" || parts[3] === "continuable")) return {
292
+ if (parts.length === 5 && parts[0] === "subagent" && (parts[3] === "one-shot" || parts[3] === "continuable" || parts[3] === "unknown")) return {
299
293
  session: {
300
294
  kind: "subagent",
301
295
  parentSessionId: parts[1],
@@ -477,7 +471,7 @@ window.__ModuleLoader__.load({
477
471
  }
478
472
  //#endregion
479
473
  //#region \0dsh-css:/home/runner/work/deepseek-harness/deepseek-harness/packages/client/ui-plan/src/client/PlanModeControl.module.css.mjs
480
- const css = ".rS3zOq_wrap{align-items:center;gap:6px;display:inline-flex}.rS3zOq_chip{corner-shape:round;background:var(--dsw-alias-state-warn-tertiary);min-width:34px;color:var(--dsw-alias-state-warn-label);cursor:pointer;border:none;border-radius:999px;align-items:center;gap:4px;padding:2px 8px;font-size:13px;font-weight:500;line-height:20px;display:inline-flex}.rS3zOq_chip:hover:not(:disabled){color:var(--dsw-alias-state-warn-primary)}.rS3zOq_chip:focus-visible{outline:2px solid var(--dsw-alias-state-warn-label);outline-offset:2px}.rS3zOq_chip:disabled{opacity:.6;cursor:default}.rS3zOq_close{color:currentColor;align-items:center;display:inline-flex}.rS3zOq_error{color:var(--dsw-alias-state-error-primary);font-size:12px;line-height:18px}";
474
+ const css = ".rS3zOq_wrap{align-items:center;gap:6px;display:inline-flex}.rS3zOq_chip{background:var(--dsw-alias-state-business-tertiary);min-width:34px;height:28px;color:var(--dsw-alias-state-business-primary);cursor:pointer;border:none;border-radius:14px;align-items:center;gap:4px;padding:0 8px;font-size:13px;font-weight:500;line-height:20px;display:inline-flex}.rS3zOq_chip:hover:not(:disabled){background:color-mix(in srgb, var(--dsw-alias-state-business-tertiary), var(--dsw-alias-state-business-primary) 6%)}.rS3zOq_chip:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:2px}.rS3zOq_chip:disabled{opacity:.6;cursor:default}.rS3zOq_glyph{color:currentColor;flex:none;width:14px;height:14px;display:inline-flex}.rS3zOq_hoverGlyph,.rS3zOq_chip:hover:not(:disabled) .rS3zOq_restGlyph,.rS3zOq_chip:focus-visible .rS3zOq_restGlyph{display:none}.rS3zOq_chip:hover:not(:disabled) .rS3zOq_hoverGlyph,.rS3zOq_chip:focus-visible .rS3zOq_hoverGlyph{display:block}.rS3zOq_error{color:var(--dsw-alias-state-error-primary);font-size:12px;line-height:18px}";
481
475
  const tagId = "@deepseek-ai/dsh-client-ui-plan/PlanModeControl.module.css";
482
476
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
483
477
  const tag = document.createElement("style");
@@ -488,8 +482,10 @@ window.__ModuleLoader__.load({
488
482
  }
489
483
  var PlanModeControl_module_css_default = {
490
484
  "chip": "rS3zOq_chip",
491
- "close": "rS3zOq_close",
492
485
  "error": "rS3zOq_error",
486
+ "glyph": "rS3zOq_glyph",
487
+ "hoverGlyph": "rS3zOq_hoverGlyph",
488
+ "restGlyph": "rS3zOq_restGlyph",
493
489
  "wrap": "rS3zOq_wrap"
494
490
  };
495
491
  //#endregion
@@ -534,11 +530,17 @@ window.__ModuleLoader__.load({
534
530
  title: t("chip.on.title"),
535
531
  disabled: locked || leaving,
536
532
  onClick: off,
537
- children: [t("chip.label"), (0, react_jsx_runtime.jsx)("span", {
538
- className: PlanModeControl_module_css_default.close,
533
+ children: [(0, react_jsx_runtime.jsxs)("span", {
534
+ className: PlanModeControl_module_css_default.glyph,
539
535
  "aria-hidden": true,
540
- children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCloseFill14, { size: 12 })
541
- })]
536
+ children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPlanOutlineRegular, {
537
+ className: PlanModeControl_module_css_default.restGlyph,
538
+ size: 14
539
+ }), (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCloseCircleFillRegular, {
540
+ className: PlanModeControl_module_css_default.hoverGlyph,
541
+ size: 14
542
+ })]
543
+ }), t("chip.label")]
542
544
  }), error !== null && (0, react_jsx_runtime.jsx)("span", {
543
545
  className: PlanModeControl_module_css_default.error,
544
546
  role: "status",
@@ -552,7 +554,7 @@ window.__ModuleLoader__.load({
552
554
  /** `plan` namespace dictionaries (the composer plan chip's copy). */
553
555
  /** Simplified Chinese dictionary (the key-set source of truth). */
554
556
  const zh = {
555
- "chip.label": "Plan",
557
+ "chip.label": "计划",
556
558
  "preview.title": "计划",
557
559
  "preview.document": "计划 · Markdown",
558
560
  "preview.action": "打开",
@@ -566,11 +568,9 @@ window.__ModuleLoader__.load({
566
568
  "preview.notFound": "未找到这份计划",
567
569
  "preview.unavailable": "计划预览不可用",
568
570
  "preview.expired": "临时计划预览已失效,请从仍在等待审批的卡片重新打开。",
569
- "chip.on.aria": "plan mode 已开启,按下关闭",
570
- "chip.on.title": "plan mode 已开启 — 点击关闭(/plan off)",
571
- "chip.off.aria": "plan mode 已关闭,按下开启",
572
- "chip.off.title": "plan mode 已关闭 — 点击开启(/plan)",
573
- "chip.exitFailed": "退出 plan mode 失败"
571
+ "chip.on.aria": "计划模式已开启,按下关闭",
572
+ "chip.on.title": "计划模式已开启 — 点击关闭(/plan off)",
573
+ "chip.exitFailed": "退出计划模式失败"
574
574
  };
575
575
  /** English dictionary, checked complete against the zh key set. */
576
576
  const en = {
@@ -590,8 +590,6 @@ window.__ModuleLoader__.load({
590
590
  "preview.expired": "This temporary plan preview has expired. Reopen it from the pending review card.",
591
591
  "chip.on.aria": "Plan mode on, press to turn off",
592
592
  "chip.on.title": "Plan mode on — click to turn off (/plan off)",
593
- "chip.off.aria": "Plan mode off, press to turn on",
594
- "chip.off.title": "Plan mode off — click to turn on (/plan)",
595
593
  "chip.exitFailed": "Failed to exit plan mode"
596
594
  };
597
595
  //#endregion
@@ -641,7 +639,7 @@ window.__ModuleLoader__.load({
641
639
  kind: "subagent",
642
640
  ...child
643
641
  };
644
- ctx.sidebarRight.openResourceIn(sessionId, planAddress({
642
+ ctx.sidebarRight.openResource(planAddress({
645
643
  session,
646
644
  callId
647
645
  }));
@@ -652,23 +650,38 @@ window.__ModuleLoader__.load({
652
650
  name: "conversation.chat.turnTail",
653
651
  id: previewId,
654
652
  locale: NS,
655
- inject: open
653
+ inject: (sessionId) => {
654
+ const binding = ctx.sessions.binding(sessionId);
655
+ if (binding === void 0) throw new Error(`ui-plan: unknown session "${sessionId}"`);
656
+ const chat = ctx.uiConversation.binding(binding).target("chat");
657
+ return {
658
+ ...open(sessionId),
659
+ keyedHooks: { plans: (turn) => {
660
+ const snapshot = chat.getSnapshot();
661
+ if (snapshot === void 0) throw new Error("ui-plan: Chat target is unavailable");
662
+ return snapshot.nodes.turnDataSource(Number(turn), "submitted-plan");
663
+ } }
664
+ };
665
+ }
656
666
  }, PlanCards));
657
667
  ctx.slots.inject("conversation.plan-review.actions", () => ctx.slots.register({
658
668
  name: "conversation.plan-review.actions",
659
669
  id: previewId,
660
670
  locale: NS,
661
671
  store: reviewStore,
662
- inject: (sessionId) => ({ openReview: (review, requestKey) => {
663
- if (review.callId !== void 0) {
664
- open(sessionId).openPlan(review.callId);
665
- return;
666
- }
667
- ctx.sidebarRight.openResourceIn(sessionId, reviewPreviewAddress(sessionId, `${reviewWindow}:${requestKey}`), { params: { planReview: {
668
- markdown: review.plan,
669
- title: (0, _deepseek_ai_dsh_client_ui_primitives.extractMarkdownPlainText)(review.plan, { mode: "first-line" })
670
- } } });
671
- } })
672
+ inject: (sessionId) => ({
673
+ openReview: (review, requestKey) => {
674
+ if (review.callId !== void 0) {
675
+ open(sessionId).openPlan(review.callId);
676
+ return;
677
+ }
678
+ ctx.sidebarRight.openResource(reviewPreviewAddress(sessionId, `${reviewWindow}:${requestKey}`), { params: { planReview: {
679
+ markdown: review.plan,
680
+ title: (0, _deepseek_ai_dsh_client_ui_primitives.extractMarkdownPlainText)(review.plan, { mode: "first-line" })
681
+ } } });
682
+ },
683
+ hooks: { sidebarMounted: ctx.sidebarRight.mounted }
684
+ })
672
685
  }, PlanReviewOpen));
673
686
  ctx.slots.inject("sidebar.right.pane.tab", () => ctx.slots.register({
674
687
  name: "sidebar.right.pane.tab",
@@ -1,26 +1,45 @@
1
- import type { InjectFace, PropsLocale, PropsRuntime, PropsStore } from '@deepseek-ai/dsh-client-ui-slots';
1
+ import type { ObservableSnapshot } from '@deepseek-ai/dsh-client-store';
2
+ import type { SubmittedPlan } from './plan.ts';
3
+ import type { HostObservable, InjectFace, PropsLocale, PropsRuntime, PropsStore } from '@deepseek-ai/dsh-client-ui-slots';
2
4
  import type { ToolCallId } from '@deepseek-ai/dsh-llm/brand';
5
+ import type { SessionId } from '@deepseek-ai/dsh-session/types';
3
6
  import type { createPlanReviewStore } from './review-store.ts';
4
7
  /** Session-bound navigation for logged plans. */
5
8
  export interface PlanOpenInjected {
6
9
  /** Open or focus the exact submitted plan. */
7
10
  openPlan: (callId: ToolCallId) => void;
8
11
  }
12
+ /** Turn-keyed submitted plans and Session-bound navigation. */
13
+ export interface PlanCardsInjected extends PlanOpenInjected {
14
+ keyedHooks: {
15
+ /** Resolve only this Turn's submitted plans, in invocation order. */
16
+ plans: (turn: string) => ObservableSnapshot<readonly SubmittedPlan[]>;
17
+ };
18
+ }
9
19
  /** Session-bound preview navigation for one pending review. */
10
20
  export interface PlanReviewOpenInjected {
11
21
  /** Open the logged plan, or the request's temporary document when no invocation exists. */
12
22
  openReview: (review: PropsRuntime<'conversation.plan-review.actions'>['review'], requestKey: string) => void;
23
+ readonly hooks: {
24
+ /** The session whose right Sidebar seat is mounted; `undefined` while none is on screen. */
25
+ readonly sidebarMounted: HostObservable<SessionId | undefined>;
26
+ };
13
27
  }
14
28
  /**
15
29
  * Render the completed Turn's submitted plans in invocation order.
16
30
  * @param props - Logged plan, localized copy, and Session-bound navigation.
17
31
  * @returns keyboard-accessible plan cards, or null for a Turn without plans.
18
32
  */
19
- export declare function PlanCards({ turn, useChat, openPlan, t }: PropsRuntime<'conversation.chat.turnTail'> & InjectFace<PlanOpenInjected> & PropsLocale<'plan'>): import("react").JSX.Element | null;
33
+ export declare function PlanCards({ turn, usePlans, openPlan, t }: PropsRuntime<'conversation.chat.turnTail'> & InjectFace<PlanCardsInjected> & PropsLocale<'plan'>): import("react").JSX.Element | null;
20
34
  /**
21
35
  * Open each pending plan automatically and retain a manual opener without answering it.
36
+ *
37
+ * The automatic open waits for a mounted Sidebar seat: a review that arrives
38
+ * while the Conversation is off screen mounts in the same commit as the seat,
39
+ * ahead of it, and the seat binds from its own effect. Reading the bound
40
+ * session through the hook opens once that binding exists.
22
41
  * @param props - Review identity, Session store, localized copy, and navigation.
23
42
  * @returns an opener for either logged or temporary plan text.
24
43
  */
25
- export declare function PlanReviewOpen({ review, requestKey, openReview, t, useStore, actions }: PropsRuntime<'conversation.plan-review.actions'> & InjectFace<PlanReviewOpenInjected> & PropsLocale<'plan'> & PropsStore<ReturnType<typeof createPlanReviewStore>>): import("react").JSX.Element;
44
+ export declare function PlanReviewOpen({ review, requestKey, openReview, useSidebarMounted, t, useStore, actions }: PropsRuntime<'conversation.plan-review.actions'> & InjectFace<PlanReviewOpenInjected> & PropsLocale<'plan'> & PropsStore<ReturnType<typeof createPlanReviewStore>>): import("react").JSX.Element;
26
45
  //# sourceMappingURL=PlanCard.d.ts.map
@@ -1,15 +1,15 @@
1
1
  import type { PropsLocale, PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots';
2
2
  type PlanPreviewProps = PropsRuntime<'sidebar.right.pane.tab'> & PropsLocale<'plan'>;
3
3
  /**
4
- * Render the submitted plan with its complete Markdown and a copy action.
4
+ * Render the submitted plan with its complete Markdown.
5
5
  * @param props - Framework-bound tab identity, resource, and copy.
6
6
  * @returns the plan document or a localized loading/failure state.
7
7
  */
8
8
  export declare function PlanPreview({ useTabInfo, useResource, t }: PlanPreviewProps): import("react").JSX.Element;
9
9
  /**
10
- * Display a plan icon and the heading in its tab after resource recovery.
10
+ * Display a plain file icon and the heading in its tab after resource recovery.
11
11
  * @param props - Framework-bound tab identity and resource reader.
12
- * @returns a decorative plan icon followed by the recovered title or initial localized label.
12
+ * @returns a decorative file icon followed by the recovered title or initial localized label.
13
13
  */
14
14
  export declare function PlanTitle({ useTabInfo, useResource }: PropsRuntime<'sidebar.right.pane.tab.title'>): import("react").JSX.Element;
15
15
  export {};
@@ -17,8 +17,6 @@ export declare const zh: {
17
17
  'preview.expired': string;
18
18
  'chip.on.aria': string;
19
19
  'chip.on.title': string;
20
- 'chip.off.aria': string;
21
- 'chip.off.title': string;
22
20
  'chip.exitFailed': string;
23
21
  };
24
22
  /** The plan namespace key union. */
@@ -41,8 +39,6 @@ export declare const en: {
41
39
  'preview.expired': string;
42
40
  'chip.on.aria': string;
43
41
  'chip.on.title': string;
44
- 'chip.off.aria': string;
45
- 'chip.off.title': string;
46
42
  'chip.exitFailed': string;
47
43
  };
48
44
  //# sourceMappingURL=locales.d.ts.map
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@deepseek-ai/dsh-client-ui-plan",
3
3
  "description": "Plan mode controls, persistent transcript plan cards, and sidebar Markdown previews",
4
- "version": "0.1.6-alpha.2",
4
+ "version": "0.1.7-alpha.1",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -42,32 +42,32 @@
42
42
  },
43
43
  "license": "MIT",
44
44
  "peerDependencies": {
45
- "@deepseek-ai/cordis": "^4.0.2"
45
+ "@deepseek-ai/cordis": "^4.0.3"
46
46
  },
47
47
  "devDependencies": {
48
48
  "@types/react": "~18.3.1",
49
49
  "react": "^18.2.0",
50
- "@deepseek-ai/dsh-client-store": "^0.1.6-alpha.2",
51
- "@deepseek-ai/dsh-api-remotes": "^0.1.6-alpha.2",
52
- "@deepseek-ai/dsh-client-locale": "^0.1.6-alpha.2",
53
- "@deepseek-ai/dsh-client-test-runtime": "^0.1.6-alpha.2",
54
- "@deepseek-ai/dsh-client-ui-conversation": "^0.1.6-alpha.2",
55
- "@deepseek-ai/dsh-client-ui-primitives": "^0.1.6-alpha.2",
56
- "@deepseek-ai/dsh-client-ui-slots": "^0.1.6-alpha.2",
57
- "@deepseek-ai/dsh-plan-mode": "^0.1.6-alpha.2",
58
- "@deepseek-ai/dsh-session": "^0.1.6-alpha.2",
59
- "@deepseek-ai/dsh-client-ui-renderer": "^0.1.6-alpha.2",
60
- "@deepseek-ai/dsh-client-ui-session": "^0.1.6-alpha.2",
61
- "@deepseek-ai/dsh-api-session-controller": "^0.1.6-alpha.2",
62
- "@deepseek-ai/dsh-client-ui-chat": "^0.1.6-alpha.2",
63
- "@deepseek-ai/dsh-client-ui-sidebar-right": "^0.1.6-alpha.2",
64
- "@deepseek-ai/dsh-client-ui-user-questions": "^0.1.6-alpha.2",
65
- "@deepseek-ai/dsh-client-resources": "^0.1.6-alpha.2",
66
- "@deepseek-ai/dsh-typert-protocol": "^0.1.6-alpha.2",
67
- "@deepseek-ai/dsh-llm": "^0.1.6-alpha.2",
68
- "@deepseek-ai/dsh-tools": "^0.1.6-alpha.2",
69
- "@deepseek-ai/dsh-util-crypto": "^0.1.6-alpha.2",
70
- "@deepseek-ai/cordis": "^4.0.2"
50
+ "@deepseek-ai/dsh-api-remotes": "^0.1.7-alpha.1",
51
+ "@deepseek-ai/dsh-client-locale": "^0.1.7-alpha.1",
52
+ "@deepseek-ai/dsh-client-store": "^0.1.7-alpha.1",
53
+ "@deepseek-ai/dsh-client-ui-conversation": "^0.1.7-alpha.1",
54
+ "@deepseek-ai/dsh-client-ui-primitives": "^0.1.7-alpha.1",
55
+ "@deepseek-ai/dsh-plan-mode": "^0.1.7-alpha.1",
56
+ "@deepseek-ai/dsh-client-test-runtime": "^0.1.7-alpha.1",
57
+ "@deepseek-ai/dsh-client-ui-slots": "^0.1.7-alpha.1",
58
+ "@deepseek-ai/cordis": "^4.0.3",
59
+ "@deepseek-ai/dsh-session": "^0.1.7-alpha.1",
60
+ "@deepseek-ai/dsh-client-ui-renderer": "^0.1.7-alpha.1",
61
+ "@deepseek-ai/dsh-client-ui-session": "^0.1.7-alpha.1",
62
+ "@deepseek-ai/dsh-api-session-controller": "^0.1.7-alpha.1",
63
+ "@deepseek-ai/dsh-client-ui-chat": "^0.1.7-alpha.1",
64
+ "@deepseek-ai/dsh-client-ui-sidebar-right": "^0.1.7-alpha.1",
65
+ "@deepseek-ai/dsh-client-ui-user-questions": "^0.1.7-alpha.1",
66
+ "@deepseek-ai/dsh-client-resources": "^0.1.7-alpha.1",
67
+ "@deepseek-ai/dsh-typert-protocol": "^0.1.7-alpha.1",
68
+ "@deepseek-ai/dsh-llm": "^0.1.7-alpha.1",
69
+ "@deepseek-ai/dsh-tools": "^0.1.7-alpha.1",
70
+ "@deepseek-ai/dsh-util-crypto": "^0.1.7-alpha.1"
71
71
  },
72
72
  "files": [
73
73
  "lib/index.js",