dsh-plugin-bridge 0.3.0 → 0.3.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.md CHANGED
@@ -9,8 +9,9 @@
9
9
  [![ci](https://github.com/Totoro-qaq/dsh-plugin-bridge/actions/workflows/ci.yml/badge.svg)](https://github.com/Totoro-qaq/dsh-plugin-bridge/actions/workflows/ci.yml)
10
10
  [![license](https://img.shields.io/badge/license-MIT-green)](LICENSE)
11
11
  [![node ≥22](https://img.shields.io/badge/node-%E2%89%A522-339933)](package.json)
12
- [![dsh rc.6 → 0.1.1-rc.2](https://img.shields.io/badge/dsh-rc.6%20%E2%86%92%200.1.1--rc.2-4c8dff)](https://github.com/deepseek-ai/deepseek-harness)
12
+ [![dsh rc.6 → 0.1.2-alpha.5](https://img.shields.io/badge/dsh-rc.6%20%E2%86%92%200.1.2--alpha.5-4c8dff)](https://github.com/deepseek-ai/deepseek-harness)
13
13
  [![Listed in Awesome DSH Plugin](https://img.shields.io/badge/listed_in-Awesome_DSH_Plugin-2ea44f)](https://github.com/awesome-dsh-plugin/awesome-dsh-plugin)
14
+ [![dshfind](https://dshfind.com/api/badge/Totoro-qaq/dsh-plugin-bridge?lang=en)](https://dshfind.com/en/plugins/Totoro-qaq/dsh-plugin-bridge?ref=badge)
14
15
 
15
16
  English | [中文](README.zh.md)
16
17
 
@@ -34,7 +35,7 @@ dsh plugin --profile web add dsh-plugin-bridge
34
35
  Pinned GitHub fallback:
35
36
 
36
37
  ```bash
37
- dsh plugin --profile web add github:Totoro-qaq/dsh-plugin-bridge#v0.3.0
38
+ dsh plugin --profile web add github:Totoro-qaq/dsh-plugin-bridge#v0.3.2
38
39
  ```
39
40
 
40
41
  Then type in the official WebUI:
@@ -47,7 +48,7 @@ Then type in the official WebUI:
47
48
  /bridge code --go --continue restate and start work in the same target request
48
49
  ```
49
50
 
50
- On DSH rc.7 and later, the official WebUI renders `/bridge` as a native card. **Text** exposes the fixed five sections as ordinary fields and list rows; **Markdown** preserves full source freedom; **Preview** renders Markdown or a complete JSON tree. Long content scrolls inside the card while the action bar stays reachable. **Confirm migration** opens the created target session.
51
+ On DSH rc.7 and later, the official WebUI renders `/bridge` as a native card. **Text** exposes the fixed five sections as ordinary fields and flat bullet or ordered-list rows while preserving their Markdown markers; **Markdown** preserves full source freedom; **Preview** renders Markdown or a complete JSON tree. Long content scrolls inside the card while the action bar stays reachable. **Confirm migration** opens the created target session.
51
52
 
52
53
  UIs that implement the official `conversation.chat.commandview` slot receive the same card automatically. Other custom UIs retain the complete server result, summary-file workflow, and target title/session-ID fallback; UI authors can reuse the framework-free `dsh-plugin-bridge/client-contract` export instead of reimplementing the wire. On an older or non-slot client, correct the printed summary file and run:
53
54
 
@@ -55,6 +56,8 @@ UIs that implement the official `conversation.chat.commandview` slot receive the
55
56
  /bridge code --go --file <path>
56
57
  ```
57
58
 
59
+ Preview edits are temporary until migration is confirmed. Restarting the client or system may discard them; the source session remains untouched, and you can regenerate the preview.
60
+
58
61
  Uninstall with `dsh plugin --profile web remove dsh-plugin-bridge`, then restart `dsh web`.
59
62
 
60
63
  ## Why Bridge
@@ -81,6 +84,7 @@ The release gate is intentionally small and reproducible; these are regression r
81
84
  | Confirm extra, paired nominal median | **+8.1%** vs `--continue` |
82
85
  | Summary worker share of clean acceptance components | **20.74% nominal** |
83
86
  | Native WebUI repeat gate (preview / target facts) | **3/3 · 3/3**, five facts each |
87
+ | DSH 0.1.2 alpha.2 / alpha.3 / alpha.5 installed WebUI | **13/13 · ordered-list edit · PTC paused · image → text fallback** |
84
88
 
85
89
  The token percentage varies widely with preset, response length, and cache state. The worker share is composition, not causal overhead versus no Bridge; the stable product claim is one additional confirmation request. Read the [design and evidence boundaries](docs/design.md), [full release report](reports/v0.2.3-e2e-report.md), and [vision report](reports/v0.2.6-rc11-vision-report.md).
86
90
 
@@ -113,6 +117,7 @@ The five sections are Goal, Current state, Key decisions and conventions, Key fi
113
117
  | 0.1.0-rc.6 | Yes | No | Narrow RPC contract and text compatibility tests |
114
118
  | 0.1.0-rc.7 / rc.8 | Yes | Contract-checked | Client-module/command-slot contract plus server fallback |
115
119
  | 0.1.1-rc.2 | Yes | Yes | Installed official WebUI: doctor 13/13, edit/confirm/auto-open, three-run repeat gate |
120
+ | 0.1.2-alpha.2 / alpha.3 / alpha.5 | Yes | Yes | Official DSH npm hosts: typed controllers 13/13 and PTC auto-open; the alpha.5 gate installed the branch tarball, retained alpha.3 titles, edited ordered lists, fell back from unresolved image to text, and removed cleanly |
116
121
 
117
122
  CI covers Node.js 22 and 24. Run `/bridge --doctor` after every Harness upgrade; it names missing required gateway methods instead of failing vaguely.
118
123
 
@@ -133,6 +138,7 @@ The server command stays the compatibility core. The same package now adds an op
133
138
  - [Release acceptance report](reports/v0.2.3-e2e-report.md)
134
139
  - [Vision migration report](reports/v0.2.6-rc11-vision-report.md)
135
140
  - [Native WebUI repeat acceptance](reports/native-workbench-2026-08-25.md)
141
+ - [DSH 0.1.2-alpha.2 compatibility acceptance](reports/dsh-0.1.2-alpha.2-compat-2026-08-31.md)
136
142
  - [Historical compression benchmark](docs/benchmark.md)
137
143
 
138
144
  ## Development
@@ -142,7 +148,7 @@ npm ci
142
148
  npm run verify
143
149
  ```
144
150
 
145
- `verify` builds and type-checks both plugin halves, runs 160 tests, checks generated `lib/` and datasets, then packs, installs, and imports the actual npm tarball. Tests spend no model tokens. `prepublishOnly` runs the same gate; GitHub releases also require the tag to match `package.json` before trusted npm publishing.
151
+ `verify` builds and type-checks both plugin halves, runs 170 tests, checks generated `lib/` and datasets, then packs, installs, and imports the actual npm tarball. Tests spend no model tokens. `prepublishOnly` runs the same gate; GitHub releases also require the tag to match `package.json` before trusted npm publishing.
146
152
 
147
153
  Community listings: [Awesome DSH Plugin](https://github.com/awesome-dsh-plugin/awesome-dsh-plugin) · [Awesome DeepSeek Harness](https://github.com/Dominic789654/awesome-deepseek-harness)
148
154
 
package/README.zh.md CHANGED
@@ -9,8 +9,9 @@
9
9
  [![ci](https://github.com/Totoro-qaq/dsh-plugin-bridge/actions/workflows/ci.yml/badge.svg)](https://github.com/Totoro-qaq/dsh-plugin-bridge/actions/workflows/ci.yml)
10
10
  [![license](https://img.shields.io/badge/license-MIT-green)](LICENSE)
11
11
  [![node ≥22](https://img.shields.io/badge/node-%E2%89%A522-339933)](package.json)
12
- [![dsh rc.6 → 0.1.1-rc.2](https://img.shields.io/badge/dsh-rc.6%20%E2%86%92%200.1.1--rc.2-4c8dff)](https://github.com/deepseek-ai/deepseek-harness)
12
+ [![dsh rc.6 → 0.1.2-alpha.5](https://img.shields.io/badge/dsh-rc.6%20%E2%86%92%200.1.2--alpha.5-4c8dff)](https://github.com/deepseek-ai/deepseek-harness)
13
13
  [![收录于 Awesome DSH Plugin](https://img.shields.io/badge/%E5%B7%B2%E6%94%B6%E5%BD%95-Awesome_DSH_Plugin-2ea44f)](https://github.com/awesome-dsh-plugin/awesome-dsh-plugin)
14
+ [![dshfind](https://dshfind.com/api/badge/Totoro-qaq/dsh-plugin-bridge?lang=zh)](https://dshfind.com/zh/plugins/Totoro-qaq/dsh-plugin-bridge?ref=badge)
14
15
 
15
16
  [English](README.md) | 中文
16
17
 
@@ -34,7 +35,7 @@ dsh plugin --profile web add dsh-plugin-bridge
34
35
  GitHub 固定版本备用路径:
35
36
 
36
37
  ```bash
37
- dsh plugin --profile web add github:Totoro-qaq/dsh-plugin-bridge#v0.3.0
38
+ dsh plugin --profile web add github:Totoro-qaq/dsh-plugin-bridge#v0.3.2
38
39
  ```
39
40
 
40
41
  然后在官方 WebUI 输入:
@@ -47,7 +48,7 @@ dsh plugin --profile web add github:Totoro-qaq/dsh-plugin-bridge#v0.3.0
47
48
  /bridge code --go --continue 在同一次目标请求里复述并开始工作
48
49
  ```
49
50
 
50
- DSH rc.7 及以上会在官方 WebUI 原生卡片中渲染 `/bridge`。「文本编辑」把固定五段变成普通文本框和逐条列表;「Markdown」保留完整源码自由;「预览」渲染 Markdown 或完整 JSON 树。长内容只在卡片正文内滚动,操作按钮保持可达;点「确认迁移」后自动打开目标会话。
51
+ DSH rc.7 及以上会在官方 WebUI 原生卡片中渲染 `/bridge`。「文本编辑」把固定五段变成普通文本框,并无损支持平坦的项目符号或编号列表;「Markdown」保留完整源码自由;「预览」渲染 Markdown 或完整 JSON 树。长内容只在卡片正文内滚动,操作按钮保持可达;点「确认迁移」后自动打开目标会话。
51
52
 
52
53
  实现官方 `conversation.chat.commandview` slot 的第三方 UI 会自动得到同一张卡片。其他自定义 UI 仍保留完整服务端结果、摘要文件流程和目标标题/session ID 回退;UI 作者还可以复用无 React 的 `dsh-plugin-bridge/client-contract` 导出,而无需重写协议。旧客户端可修改输出里打印的摘要文件,再执行:
53
54
 
@@ -55,6 +56,8 @@ DSH rc.7 及以上会在官方 WebUI 原生卡片中渲染 `/bridge`。「文本
55
56
  /bridge code --go --file <路径>
56
57
  ```
57
58
 
59
+ 预览中的修改在确认迁移前只是临时草稿。重启客户端或系统可能使其丢失;源会话始终不受影响,重新运行预览即可生成新的草稿。
60
+
58
61
  卸载:`dsh plugin --profile web remove dsh-plugin-bridge`,然后重启 `dsh web`。
59
62
 
60
63
  ## 为什么是 Bridge
@@ -81,6 +84,7 @@ DSH rc.7 及以上会在官方 WebUI 原生卡片中渲染 `/bridge`。「文本
81
84
  | Confirm 相对 `--continue` 的 nominal 配对中位额外成本 | **+8.1%** |
82
85
  | 摘要 worker 在干净验收组件中的 nominal 占比 | **20.74%** |
83
86
  | 原生 WebUI 重复门禁(预览 / 目标事实) | **3/3 · 3/3**,每次五项 |
87
+ | DSH 0.1.2 alpha.2 / alpha.3 / alpha.5 官方 WebUI 实装 | **13/13 · 编号列表编辑 · PTC 已暂停 · 图片转文本降级** |
84
88
 
85
89
  token 百分比会随 preset、回复长度和缓存状态大幅波动;worker 占比是组成,不是相对“无 Bridge”的因果开销。稳定结论是默认确认多一个请求。边界和原始证据见[设计与证据说明](docs/design.md)、[完整 release report](reports/v0.2.3-e2e-report.md)和[视觉迁移报告](reports/v0.2.6-rc11-vision-report.md)。
86
90
 
@@ -113,6 +117,7 @@ token 百分比会随 preset、回复长度和缓存状态大幅波动;worker
113
117
  | 0.1.0-rc.6 | 支持 | 不支持 | 窄 RPC 契约与文本兼容测试 |
114
118
  | 0.1.0-rc.7 / rc.8 | 支持 | 契约核对 | client module / command slot 契约与服务端回退 |
115
119
  | 0.1.1-rc.2 | 支持 | 支持 | 官方 WebUI 实装:doctor 13/13、编辑/确认/自动跳转、三次重复门禁 |
120
+ | 0.1.2-alpha.2 / alpha.3 / alpha.5 | 支持 | 支持 | 官方 DSH npm 宿主:typed controllers 13/13 与 PTC 自动跳转;alpha.5 门禁安装分支 tarball,保留 alpha.3 标题,编号列表可编辑,未解析图片降级为文本,卸载干净 |
116
121
 
117
122
  CI 覆盖 Node.js 22/24。每次升级 Harness 后先跑 `/bridge --doctor`;缺哪个必要网关方法会被直接点名。
118
123
 
@@ -133,6 +138,7 @@ CI 覆盖 Node.js 22/24。每次升级 Harness 后先跑 `/bridge --doctor`;
133
138
  - [完整 release acceptance](reports/v0.2.3-e2e-report.md)
134
139
  - [视觉迁移报告](reports/v0.2.6-rc11-vision-report.md)
135
140
  - [原生 WebUI 重复验收](reports/native-workbench-2026-08-25.md)
141
+ - [DSH 0.1.2-alpha.2 兼容性验收](reports/dsh-0.1.2-alpha.2-compat-2026-08-31.md)
136
142
  - [历史压缩档位 benchmark](docs/benchmark.md)
137
143
 
138
144
  ## 开发验证
@@ -142,7 +148,7 @@ npm ci
142
148
  npm run verify
143
149
  ```
144
150
 
145
- `verify` 会构建并类型检查插件两端、运行 160 项测试、核对 `lib/` 与数据集,再把真实 npm tarball 打包、安装并导入。测试不消耗模型 token。`prepublishOnly` 使用同一个 gate;GitHub Release 还会先检查 tag 与 `package.json` 版本一致,再走可信 npm 发布。
151
+ `verify` 会构建并类型检查插件两端、运行 170 项测试、核对 `lib/` 与数据集,再把真实 npm tarball 打包、安装并导入。测试不消耗模型 token。`prepublishOnly` 使用同一个 gate;GitHub Release 还会先检查 tag 与 `package.json` 版本一致,再走可信 npm 发布。
146
152
 
147
153
  社区收录:[Awesome DSH Plugin](https://github.com/awesome-dsh-plugin/awesome-dsh-plugin) · [Awesome DeepSeek Harness](https://github.com/Dominic789654/awesome-deepseek-harness)
148
154
 
package/docs/guide.zh.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  ## 1. 安装
6
6
 
7
- 前置:已安装 dsh(`dsh --version` 能输出版本,本插件已核对 0.1.0-rc.6 / rc.7 / rc.8,并在 0.1.1-rc.2 完成真实视觉迁移),并有一个可跑的 web profile(跑过一次 `dsh web` 即会自动初始化)。
7
+ 前置:已安装 dsh(`dsh --version` 能输出版本,本插件已核对 0.1.0-rc.6 / rc.7 / rc.8,在 0.1.1-rc.2 完成真实视觉迁移,并用官方 npm `0.1.2-alpha.2` 完成 typed-controller 安装、编辑、迁移与卸载验收),并有一个可跑的 web profile(跑过一次 `dsh web` 即会自动初始化)。
8
8
 
9
9
  ```bash
10
10
  dsh plugin --profile web add dsh-plugin-bridge
@@ -13,7 +13,7 @@ dsh plugin --profile web add dsh-plugin-bridge
13
13
  需要固定 GitHub tag 或 npm 暂时不可用时:
14
14
 
15
15
  ```bash
16
- dsh plugin --profile web add github:Totoro-qaq/dsh-plugin-bridge#v0.3.0
16
+ dsh plugin --profile web add github:Totoro-qaq/dsh-plugin-bridge#v0.3.2
17
17
  ```
18
18
 
19
19
  发生了什么(不用手动干预):
@@ -138,10 +138,10 @@ dsh-bridge migrate --to code --summary-file <path>
138
138
  ## 6. 常见问题
139
139
 
140
140
  **Q:`/bridge` 打了没反应 / 提示未知命令?**
141
- A:确认重启过 `dsh web`(插件在启动时挂载),并且用的是 `web` profile。命令注册依赖 `commands` 服务、执行依赖 `apiProxy` 服务,两者在官方 web profile 里都在;缺其一插件会挂起等待而不是半挂,日志里能看到。
141
+ A:确认重启过 `dsh web`(插件在启动时挂载),并且用的是 `web` profile。命令注册依赖 `commands` 服务;执行时优先用 rc.2 的 `apiProxy`,alpha 则使用 typed Session / Workspace / Preset / Goal controllers。必要能力不完整时 `/bridge --doctor` 会逐项报告,不会假装可用。
142
142
 
143
143
  **Q:升级了 dsh 之后还能用吗?**
144
- A:先打一次 `/bridge --doctor`,它会告诉你这套 host 暴露了十三个网关方法里的哪几个、当前模式是什么、生效配置是什么。全绿就是好的。缺方法它会点名,把那行连同你的 dsh 版本发到 issues 就行。rc.6 / rc.7 / rc.8 已逐条核对;0.1.1-rc.2 的 doctor 13/13、预览、迁移、目标模型继承与原图 kickoff 均实测通过。
144
+ A:先打一次 `/bridge --doctor`,它会告诉你这套 host 暴露了十三项能力里的哪几项、当前模式是什么、生效配置是什么。全绿就是好的。缺能力它会点名,把那行连同你的 dsh 版本发到 issues 就行。rc.6 / rc.7 / rc.8 已逐条核对;0.1.1-rc.2 的原图迁移已实测;官方 npm `0.1.2-alpha.2` typed-controller 13/13、三态编辑、PTC 自动跳转、暂停目标与干净卸载也已实测。
145
145
 
146
146
  **Q:图片迁移需要怎么选模型?**
147
147
  A:图片已有助手分析时,Bridge 逐字搬这段视觉证据,不需要再次烧视觉 token。图片还没被分析时,源会话应选 `deepseek-v4-flash-vision-exp` 等视觉路由;Bridge 会在 kickoff 前把源 provider/model/reasoning effort 复制到目标,再搬原图。文本模型仍不能识图,Bridge 也不会暗中启动本地视觉模型。
@@ -4,9 +4,9 @@ Status: **implemented on main; the server command remains the compatibility fall
4
4
 
5
5
  ## Verified extension path
6
6
 
7
- The official rc.7 through `0.1.1-rc.2` WebUI contract supports third-party browser modules through a package-level `dsh.client` manifest. Its client runtime exposes a slot registry, and official plugins register occupants such as conversation views, input docks, settings items, and command renderers. Bridge uses that path; it does not patch the DOM or fork the WebUI.
7
+ The official rc.7 through `0.1.1-rc.2` WebUI and the split `0.1.2-alpha.2` client packages support third-party browser modules through a package-level `dsh.client` manifest. The renderer exposes a slot registry, and official plugins register occupants such as conversation views, input docks, settings items, and command renderers. Bridge uses that path; it does not patch the DOM or fork the WebUI.
8
8
 
9
- The host half still injects `commands` and `apiProxy`. The client half registers the `bridge` key in `conversation.chat.commandview`, parses only the host result, renders Markdown or complete JSON, offers lossless five-section Text/Markdown editing, and sends the reviewed summary back through `/bridge --go --summary64`. `recordInput: false` keeps that encoded payload out of the durable command log.
9
+ The host half hard-injects only `commands`: it prefers rc.2's `apiProxy` when present and otherwise maps alpha's typed Session, Workspace, Preset, and Goal controllers into `BridgeHost`. The client half registers the `bridge` key in `conversation.chat.commandview`, parses only the host result, renders Markdown or complete JSON, offers lossless five-section Text/Markdown editing, and sends the reviewed summary back through `/bridge --go --summary64`. `recordInput: false` keeps that encoded payload out of the durable command log.
10
10
 
11
11
  ## Product shape
12
12
 
@@ -54,7 +54,7 @@ if (card.phase === 'preview') {
54
54
  - Edited summaries are bounded to 24,000 characters and base64url-encoded for one command invocation. Malformed or oversized payloads fail closed before target creation; emptiness checks do not trim the transmitted Markdown.
55
55
  - A WebUI payload is accepted only while the exact preview ID for the matching source session and target preset is pending and unexpired. The preview is consumed before migration; retrying the same completed payload returns the first target result instead of creating another session, even if a newer preview has since been generated.
56
56
  - Once a target session exists, an unconfirmed kickoff is returned as that same navigable target with a warning and cached result. Bridge does not restore the preview and create a second target when prompt delivery is ambiguous.
57
- - The client contract shapes for rc.7, rc.8, `0.1.1-rc.1`, and `0.1.1-rc.2` were compared; `0.1.1-rc.2` has installed official-WebUI evidence.
57
+ - The client contract shapes for rc.7, rc.8, `0.1.1-rc.1`, `0.1.1-rc.2`, and `0.1.2-alpha.2` were compared. Both rc.2 and alpha.2 have installed official-WebUI evidence; alpha.2 uses the split `ui-chat`, Session Controller, renderer, and Session UI declarations.
58
58
 
59
59
  ## Acceptance evidence
60
60
 
@@ -68,3 +68,5 @@ The 2026-08-25 installed-WebUI gate covered:
68
68
  - reviewed-payload migration, paused goal, and automatic target navigation.
69
69
 
70
70
  Three fixed real-model runs preserved five facts in both preview and target, with worker times from 7.4 to 12.8 seconds. See the [native workbench report](../reports/native-workbench-2026-08-25.md). This is release evidence, not a statistical guarantee.
71
+
72
+ The 2026-08-31 npm-installed alpha.2 gate additionally covered typed-controller doctor 13/13, split-client compilation, localized primitive labels, Text-to-Markdown review, PTC auto-open, paused goal, and clean removal. See the [alpha.2 compatibility report](../reports/dsh-0.1.2-alpha.2-compat-2026-08-31.md).
@@ -43,15 +43,18 @@ export interface BridgeTextSection {
43
43
  bodyStart: number;
44
44
  bodyEnd: number;
45
45
  items?: BridgeTextListItem[];
46
- listStyle?: 'bullet' | 'plain';
46
+ listStyle?: BridgeListStyle;
47
47
  textStyle?: 'plain' | 'bullets';
48
48
  }
49
+ type BridgeListStyle = 'bullet' | 'ordered' | 'plain';
49
50
  export interface BridgeTextListItem {
50
51
  text: string;
51
52
  contentStart: number;
52
53
  contentEnd: number;
53
54
  itemStart: number;
54
55
  itemEnd: number;
56
+ /** Exact Markdown marker retained when this item is edited. */
57
+ marker?: string;
55
58
  /** Original indentation for wrapped lines, kept when that item is edited. */
56
59
  continuationPrefixes?: string[];
57
60
  }
@@ -75,7 +78,7 @@ export declare function replaceBridgeTextSection(projection: BridgeTextProjectio
75
78
  export declare function replaceBridgeTextListItem(projection: BridgeTextProjection, key: BridgeTextSectionKey, index: number, plainText: string): string;
76
79
  /** Remove exactly one original list-item span. */
77
80
  export declare function removeBridgeTextListItem(projection: BridgeTextProjection, key: BridgeTextSectionKey, index: number): string;
78
- /** Append one item using the section's existing bullet/plain convention. */
81
+ /** Append one item using the section's existing Markdown list convention. */
79
82
  export declare function appendBridgeTextListItem(projection: BridgeTextProjection, key: BridgeTextSectionKey, plainText: string): string;
80
83
  /** Map the official WebUI document language onto Bridge's supported UI copy. */
81
84
  export declare function uiLanguageOf(documentLang: string | undefined): 'zh' | 'en';
@@ -85,3 +88,4 @@ export declare function parseBridgeCard(outcome: BridgeOutcome): BridgeCard;
85
88
  export declare function parseJsonDocument(text: string): object | unknown[] | undefined;
86
89
  /** Build the hidden-input-safe command used by the native editor confirmation. */
87
90
  export declare function buildBridgeMigrationCommand(targetPreset: string, summary: string, lang: 'zh' | 'en', previewId: string): string;
91
+ export {};
@@ -87,6 +87,8 @@ const THEMATIC_OR_SETEXT = /^ {0,3}(?:(?:\*[\t ]*){3,}|(?:-[\t ]*){3,}|(?:_[\t ]
87
87
  const TABLE_DELIMITER = /^ {0,3}\|?[\t ]*:?-{3,}:?[\t ]*(?:\|[\t ]*:?-{3,}:?[\t ]*)+\|?[\t ]*$/u;
88
88
  const LINK_DEFINITION = /^ {0,3}\[[^\]]+\]:[\t ]*\S/u;
89
89
  const HTML_BLOCK_START = /^ {0,3}<[/!?A-Za-z]/u;
90
+ const BULLET_LIST_ITEM = /^(- )(.+)$/u;
91
+ const ORDERED_LIST_ITEM = /^(\d{1,9}[.)] )(.+)$/u;
90
92
  function hasUnsafeBlock(line) {
91
93
  return MARKDOWN_BLOCK_START.test(line)
92
94
  || THEMATIC_OR_SETEXT.test(line)
@@ -104,8 +106,14 @@ function parseListItems(markdownBody, lineEnding, bodyStart, allowPlain) {
104
106
  const lines = sourceLines(markdownBody, lineEnding);
105
107
  if (lines.some((line) => !line.text.trim()))
106
108
  return undefined;
107
- const bullet = /^- (.+)$/u.exec(lines[0]?.text ?? '');
108
- const style = bullet ? 'bullet' : allowPlain ? 'plain' : undefined;
109
+ const firstLine = lines[0]?.text ?? '';
110
+ const style = BULLET_LIST_ITEM.test(firstLine)
111
+ ? 'bullet'
112
+ : ORDERED_LIST_ITEM.test(firstLine)
113
+ ? 'ordered'
114
+ : allowPlain
115
+ ? 'plain'
116
+ : undefined;
109
117
  if (!style)
110
118
  return undefined;
111
119
  const items = [];
@@ -123,9 +131,10 @@ function parseListItems(markdownBody, lineEnding, bodyStart, allowPlain) {
123
131
  }
124
132
  }
125
133
  else {
134
+ const itemPattern = style === 'ordered' ? ORDERED_LIST_ITEM : BULLET_LIST_ITEM;
126
135
  const starts = [];
127
136
  for (const [index, line] of lines.entries()) {
128
- if (/^- (.+)$/u.test(line.text)) {
137
+ if (itemPattern.test(line.text)) {
129
138
  starts.push(index);
130
139
  continue;
131
140
  }
@@ -137,18 +146,21 @@ function parseListItems(markdownBody, lineEnding, bodyStart, allowPlain) {
137
146
  const first = lines[lineIndex];
138
147
  const nextLineIndex = starts[itemIndex + 1] ?? lines.length;
139
148
  const last = lines[nextLineIndex - 1];
140
- const firstText = /^- (.+)$/u.exec(first.text)?.[1];
141
- if (!firstText)
149
+ const itemMatch = itemPattern.exec(first.text);
150
+ const marker = itemMatch?.[1];
151
+ const firstText = itemMatch?.[2];
152
+ if (!marker || !firstText)
142
153
  return undefined;
143
154
  const continuationLines = lines.slice(lineIndex + 1, nextLineIndex);
144
155
  const continuationPrefixes = continuationLines.map((line) => /^[\t ]*/u.exec(line.text)?.[0] ?? '');
145
156
  const continuation = continuationLines.map((line, index) => line.text.slice(continuationPrefixes[index]?.length ?? 0));
146
157
  items.push({
147
158
  text: [firstText, ...continuation].join(lineEnding),
148
- contentStart: bodyStart + first.start + 2,
159
+ contentStart: bodyStart + first.start + marker.length,
149
160
  contentEnd: bodyStart + last.contentEnd,
150
161
  itemStart: bodyStart + first.start,
151
162
  itemEnd: bodyStart + (lines[nextLineIndex]?.start ?? markdownBody.length),
163
+ marker,
152
164
  continuationPrefixes,
153
165
  });
154
166
  }
@@ -269,7 +281,7 @@ export function replaceBridgeTextSection(projection, key, plainText) {
269
281
  }
270
282
  markdownBody = section.listStyle === 'plain'
271
283
  ? values.join(projection.lineEnding)
272
- : values.map((line) => `- ${line}`).join(projection.lineEnding);
284
+ : values.map((line, index) => `${section.items?.[index]?.marker ?? '- '}${line}`).join(projection.lineEnding);
273
285
  }
274
286
  else {
275
287
  markdownBody = section.textStyle === 'bullets'
@@ -296,7 +308,7 @@ function editedListItem(text, style, lineEnding, original) {
296
308
  throw new Error('Bridge list item contains Markdown block structure');
297
309
  if (style === 'plain' && lines.length > 1)
298
310
  throw new Error('Plain path rows must stay on one line');
299
- return style === 'bullet'
311
+ return style !== 'plain'
300
312
  ? [
301
313
  lines[0],
302
314
  ...lines.slice(1).map((line, index) => `${original?.continuationPrefixes?.[index] ?? ' '}${line}`),
@@ -326,12 +338,24 @@ export function removeBridgeTextListItem(projection, key, index) {
326
338
  : item.itemStart;
327
339
  return projection.markdown.slice(0, removeStart) + projection.markdown.slice(item.itemEnd);
328
340
  }
329
- /** Append one item using the section's existing bullet/plain convention. */
341
+ /** Append one item using the section's existing Markdown list convention. */
330
342
  export function appendBridgeTextListItem(projection, key, plainText) {
331
343
  const section = listSection(projection, key);
332
344
  const edited = editedListItem(plainText, section.listStyle, projection.lineEnding);
333
345
  const prefix = section.bodyStart === section.bodyEnd ? '' : projection.lineEnding;
334
- const item = section.listStyle === 'bullet' ? `- ${edited}` : edited;
346
+ let marker = '';
347
+ if (section.listStyle === 'bullet')
348
+ marker = '- ';
349
+ if (section.listStyle === 'ordered') {
350
+ const previous = section.items?.at(-1)?.marker;
351
+ const match = /^(\d{1,9})([.)]) $/u.exec(previous ?? '');
352
+ const next = match ? Number(match[1]) + 1 : (section.items?.length ?? 0) + 1;
353
+ if (!Number.isSafeInteger(next) || next > 999_999_999) {
354
+ throw new Error('Bridge ordered list marker is out of range');
355
+ }
356
+ marker = `${String(next)}${match?.[2] ?? '.'} `;
357
+ }
358
+ const item = `${marker}${edited}`;
335
359
  return projection.markdown.slice(0, section.bodyEnd) + prefix + item + projection.markdown.slice(section.bodyEnd);
336
360
  }
337
361
  function previewHeaderOf(line) {
package/lib/client.d.ts CHANGED
@@ -1,12 +1,13 @@
1
1
  /** Official WebUI half: one native `/bridge` command card, not a second WebUI. */
2
- import type { PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots';
3
- import type { ClientContext, SessionId } from '@deepseek-ai/dsh-client-runtime/client';
2
+ import type { Context as ClientContext } from '@deepseek-ai/cordis';
3
+ import type { SessionId } from '@deepseek-ai/dsh-api-remotes/client';
4
+ import type { CommandRowProps } from '@deepseek-ai/dsh-client-ui-chat/client';
4
5
  import { type BridgeOutcome } from './client-contract.ts';
5
6
  interface BridgeInjected {
6
7
  readonly execute: (sessionId: SessionId, line: string) => Promise<BridgeOutcome>;
7
8
  readonly openSession: (sessionId: SessionId) => Promise<void>;
8
9
  }
9
- type BridgeCommandCardProps = PropsRuntime<'conversation.chat.commandview'> & BridgeInjected;
10
+ type BridgeCommandCardProps = CommandRowProps & BridgeInjected;
10
11
  /** Rich renderer for the durable command lifecycle keyed by name and isolated from every other plugin. */
11
12
  export declare function BridgeCommandCard(props: BridgeCommandCardProps): import("react").JSX.Element;
12
13
  /** Client services are supplied by the official WebUI module table. */
package/lib/client.js CHANGED
@@ -148,6 +148,8 @@ window.__ModuleLoader__.load({
148
148
  const TABLE_DELIMITER = /^ {0,3}\|?[\t ]*:?-{3,}:?[\t ]*(?:\|[\t ]*:?-{3,}:?[\t ]*)+\|?[\t ]*$/u;
149
149
  const LINK_DEFINITION = /^ {0,3}\[[^\]]+\]:[\t ]*\S/u;
150
150
  const HTML_BLOCK_START = /^ {0,3}<[/!?A-Za-z]/u;
151
+ const BULLET_LIST_ITEM = /^(- )(.+)$/u;
152
+ const ORDERED_LIST_ITEM = /^(\d{1,9}[.)] )(.+)$/u;
151
153
  function hasUnsafeBlock(line) {
152
154
  return MARKDOWN_BLOCK_START.test(line) || THEMATIC_OR_SETEXT.test(line) || TABLE_DELIMITER.test(line) || LINK_DEFINITION.test(line) || /^(?: {4}|\t)/u.test(line) || line.includes("<!--") || line.includes("-->") || line.includes("--!>") || HTML_BLOCK_START.test(line);
153
155
  }
@@ -159,7 +161,8 @@ window.__ModuleLoader__.load({
159
161
  };
160
162
  const lines = sourceLines(markdownBody, lineEnding);
161
163
  if (lines.some((line) => !line.text.trim())) return void 0;
162
- const style = /^- (.+)$/u.exec(lines[0]?.text ?? "") ? "bullet" : allowPlain ? "plain" : void 0;
164
+ const firstLine = lines[0]?.text ?? "";
165
+ const style = BULLET_LIST_ITEM.test(firstLine) ? "bullet" : ORDERED_LIST_ITEM.test(firstLine) ? "ordered" : allowPlain ? "plain" : void 0;
163
166
  if (!style) return void 0;
164
167
  const items = [];
165
168
  if (style === "plain") for (const line of lines) {
@@ -173,9 +176,10 @@ window.__ModuleLoader__.load({
173
176
  });
174
177
  }
175
178
  else {
179
+ const itemPattern = style === "ordered" ? ORDERED_LIST_ITEM : BULLET_LIST_ITEM;
176
180
  const starts = [];
177
181
  for (const [index, line] of lines.entries()) {
178
- if (/^- (.+)$/u.test(line.text)) {
182
+ if (itemPattern.test(line.text)) {
179
183
  starts.push(index);
180
184
  continue;
181
185
  }
@@ -185,17 +189,20 @@ window.__ModuleLoader__.load({
185
189
  const first = lines[lineIndex];
186
190
  const nextLineIndex = starts[itemIndex + 1] ?? lines.length;
187
191
  const last = lines[nextLineIndex - 1];
188
- const firstText = /^- (.+)$/u.exec(first.text)?.[1];
189
- if (!firstText) return void 0;
192
+ const itemMatch = itemPattern.exec(first.text);
193
+ const marker = itemMatch?.[1];
194
+ const firstText = itemMatch?.[2];
195
+ if (!marker || !firstText) return void 0;
190
196
  const continuationLines = lines.slice(lineIndex + 1, nextLineIndex);
191
197
  const continuationPrefixes = continuationLines.map((line) => /^[\t ]*/u.exec(line.text)?.[0] ?? "");
192
198
  const continuation = continuationLines.map((line, index) => line.text.slice(continuationPrefixes[index]?.length ?? 0));
193
199
  items.push({
194
200
  text: [firstText, ...continuation].join(lineEnding),
195
- contentStart: bodyStart + first.start + 2,
201
+ contentStart: bodyStart + first.start + marker.length,
196
202
  contentEnd: bodyStart + last.contentEnd,
197
203
  itemStart: bodyStart + first.start,
198
204
  itemEnd: bodyStart + (lines[nextLineIndex]?.start ?? markdownBody.length),
205
+ marker,
199
206
  continuationPrefixes
200
207
  });
201
208
  }
@@ -295,7 +302,7 @@ window.__ModuleLoader__.load({
295
302
  if (section.kind === "list") {
296
303
  const values = normalized.split(projection.lineEnding);
297
304
  if (!section.items || values.length !== section.items.length || section.items.some((item) => item.text.includes(projection.lineEnding))) throw new Error("Use Bridge list-item helpers for this section");
298
- markdownBody = section.listStyle === "plain" ? values.join(projection.lineEnding) : values.map((line) => `- ${line}`).join(projection.lineEnding);
305
+ markdownBody = section.listStyle === "plain" ? values.join(projection.lineEnding) : values.map((line, index) => `${section.items?.[index]?.marker ?? "- "}${line}`).join(projection.lineEnding);
299
306
  } else markdownBody = section.textStyle === "bullets" ? normalized.split(projection.lineEnding).map((line) => `- ${line}`).join(projection.lineEnding) : markdownSafePlainText(normalized, projection.lineEnding);
300
307
  return projection.markdown.slice(0, section.bodyStart) + markdownBody + projection.markdown.slice(section.bodyEnd);
301
308
  }
@@ -310,7 +317,7 @@ window.__ModuleLoader__.load({
310
317
  const lines = normalized.split(lineEnding);
311
318
  if (lines.some((line) => hasUnsafeBlock(line))) throw new Error("Bridge list item contains Markdown block structure");
312
319
  if (style === "plain" && lines.length > 1) throw new Error("Plain path rows must stay on one line");
313
- return style === "bullet" ? [lines[0], ...lines.slice(1).map((line, index) => `${original?.continuationPrefixes?.[index] ?? " "}${line}`)].join(lineEnding) : normalized;
320
+ return style !== "plain" ? [lines[0], ...lines.slice(1).map((line, index) => `${original?.continuationPrefixes?.[index] ?? " "}${line}`)].join(lineEnding) : normalized;
314
321
  }
315
322
  /** Replace one list item without rewriting siblings and while retaining wrapped-line indentation. */
316
323
  function replaceBridgeTextListItem(projection, key, index, plainText) {
@@ -329,12 +336,21 @@ window.__ModuleLoader__.load({
329
336
  const removeStart = index > 0 && index === items.length - 1 ? item.itemStart - projection.lineEnding.length : item.itemStart;
330
337
  return projection.markdown.slice(0, removeStart) + projection.markdown.slice(item.itemEnd);
331
338
  }
332
- /** Append one item using the section's existing bullet/plain convention. */
339
+ /** Append one item using the section's existing Markdown list convention. */
333
340
  function appendBridgeTextListItem(projection, key, plainText) {
334
341
  const section = listSection(projection, key);
335
342
  const edited = editedListItem(plainText, section.listStyle, projection.lineEnding);
336
343
  const prefix = section.bodyStart === section.bodyEnd ? "" : projection.lineEnding;
337
- const item = section.listStyle === "bullet" ? `- ${edited}` : edited;
344
+ let marker = "";
345
+ if (section.listStyle === "bullet") marker = "- ";
346
+ if (section.listStyle === "ordered") {
347
+ const previous = section.items?.at(-1)?.marker;
348
+ const match = /^(\d{1,9})([.)]) $/u.exec(previous ?? "");
349
+ const next = match ? Number(match[1]) + 1 : (section.items?.length ?? 0) + 1;
350
+ if (!Number.isSafeInteger(next) || next > 999999999) throw new Error("Bridge ordered list marker is out of range");
351
+ marker = `${String(next)}${match?.[2] ?? "."} `;
352
+ }
353
+ const item = `${marker}${edited}`;
338
354
  return projection.markdown.slice(0, section.bodyEnd) + prefix + item + projection.markdown.slice(section.bodyEnd);
339
355
  }
340
356
  function previewHeaderOf(line) {
@@ -569,6 +585,50 @@ window.__ModuleLoader__.load({
569
585
  stalePreview: "This older preview has no secure confirmation ID. Run /bridge again or use the summary-file workflow."
570
586
  }
571
587
  };
588
+ const PRIMITIVE_LABELS = {
589
+ zh: {
590
+ markdown: {
591
+ code: {
592
+ copyLabel: "复制",
593
+ copiedLabel: "已复制"
594
+ },
595
+ footnotes: "脚注"
596
+ },
597
+ json: {
598
+ copyValue: "复制值",
599
+ copyJson: "复制 JSON",
600
+ copyPath: "复制属性路径",
601
+ copyPrettyJson: "复制格式化 JSON",
602
+ copyCompactJson: "复制紧凑 JSON",
603
+ copied: "已复制",
604
+ copyFailed: "复制失败",
605
+ collapseNode: "折叠 JSON 节点",
606
+ expandNode: "展开 JSON 节点",
607
+ copyButtonTitle: (action) => `${action};右键查看更多复制选项`
608
+ }
609
+ },
610
+ en: {
611
+ markdown: {
612
+ code: {
613
+ copyLabel: "Copy",
614
+ copiedLabel: "Copied"
615
+ },
616
+ footnotes: "Footnotes"
617
+ },
618
+ json: {
619
+ copyValue: "Copy value",
620
+ copyJson: "Copy JSON",
621
+ copyPath: "Copy property path",
622
+ copyPrettyJson: "Copy pretty JSON",
623
+ copyCompactJson: "Copy compact JSON",
624
+ copied: "Copied",
625
+ copyFailed: "Copy failed",
626
+ collapseNode: "Collapse JSON node",
627
+ expandNode: "Expand JSON node",
628
+ copyButtonTitle: (action) => `${action}; right-click for copy options`
629
+ }
630
+ }
631
+ };
572
632
  function Header({ lang, route }) {
573
633
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
574
634
  className: "dsh-bridge-head",
@@ -634,9 +694,13 @@ window.__ModuleLoader__.load({
634
694
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
635
695
  className: "dsh-bridge-preview",
636
696
  "aria-label": json === void 0 ? COPY[lang].markdownPreview : COPY[lang].json,
637
- children: json === void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.MarkdownText, { text: summary }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.JsonTree, {
697
+ children: json === void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.MarkdownText, {
698
+ text: summary,
699
+ labels: PRIMITIVE_LABELS[lang].markdown
700
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.JsonTree, {
638
701
  data: json,
639
- label: COPY[lang].json
702
+ label: COPY[lang].json,
703
+ labels: PRIMITIVE_LABELS[lang].json
640
704
  })
641
705
  });
642
706
  }